Skip to content

Commit

Permalink
migrate item in recyclerview to compose (#211)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredsburrows committed Aug 7, 2022
1 parent 53bf440 commit 5690523
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 131 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ dependencies {
implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.uitooling)
implementation(libs.google.accompanist.drawablepainter)
implementation(libs.google.accompanist.webview)
androidTestImplementation(libs.androidx.compose.junit)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.burrowsapps.example.gif.ui.giflist
import android.Manifest.permission.INTERNET
import android.Manifest.permission.READ_EXTERNAL_STORAGE
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.content.Context
import android.widget.TextView
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
Expand All @@ -14,12 +15,10 @@ import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
import androidx.test.espresso.action.ViewActions.pressBack
import androidx.test.espresso.action.ViewActions.typeText
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem
import androidx.test.espresso.intent.Intents.init
import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.Intents.release
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withParent
Expand All @@ -32,6 +31,7 @@ import com.burrowsapps.example.gif.test.TestFileUtils.MOCK_SERVER_PORT
import com.burrowsapps.example.gif.test.TestFileUtils.getMockFileResponse
import com.burrowsapps.example.gif.test.TestFileUtils.getMockResponse
import com.burrowsapps.example.gif.ui.license.LicenseActivity
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.HiltTestApplication
Expand All @@ -44,13 +44,13 @@ import org.hamcrest.Matchers.containsString
import org.hamcrest.Matchers.instanceOf
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import test.ScreenshotWatcher
import java.net.HttpURLConnection.HTTP_NOT_FOUND
import javax.inject.Inject

@HiltAndroidTest
@Config(application = HiltTestApplication::class)
Expand All @@ -72,6 +72,8 @@ class GifActivityTest {
@get:Rule(order = 4)
val screenshotWatcher = ScreenshotWatcher()

@Inject @ApplicationContext internal lateinit var context: Context

private val server = MockWebServer()

@Before
Expand Down Expand Up @@ -102,13 +104,13 @@ class GifActivityTest {
}

@Test
fun testMainTitleIsShowing() {
fun testGifActivityTitleIsShowing() {
onView(
allOf(
instanceOf(TextView::class.java),
withParent(withId(R.id.toolbar))
withParent(withId(R.id.toolbar)),
)
).check(matches(withText(containsString("Top Trending Gifs"))))
).check(matches(withText(containsString(context.getString(R.string.main_screen_title)))))
}

@Test
Expand Down Expand Up @@ -145,31 +147,31 @@ class GifActivityTest {
onView(
allOf(
instanceOf(TextView::class.java),
withParent(withId(R.id.toolbar))
withParent(withId(R.id.toolbar)),
)
).check(matches(withText(containsString("Top Trending Gifs"))))

release()
}

@Ignore("on view 'Animations or transitions are enabled on the target device.")
@Test
fun testTrendingThenClickOpenDialog() {
screenshotWatcher.capture("After launch")

// Select 0, the response only contains 1 item
onView(withId(R.id.recyclerView))
.perform(
actionOnItem<GifAdapter.ViewHolder>(
hasDescendant(withId(R.id.gifImage)),
click()
).atPosition(0)
)
screenshotWatcher.capture("After click")

onView(withId(R.id.gifDialogTitle))
.perform(pressBack())
}
// @Ignore("on view 'Animations or transitions are enabled on the target device.")
// @Test
// fun testTrendingThenClickOpenDialog() {
// screenshotWatcher.capture("After launch")
//
// // Select 0, the response only contains 1 item
// onView(withId(R.id.recyclerView))
// .perform(
// actionOnItem<GifAdapter.ViewHolder>(
// hasDescendant(withId(R.id.gifImage)),
// click(),
// ).atPosition(0)
// )
// screenshotWatcher.capture("After click")
//
// onView(withId(R.id.gifDialogTitle))
// .perform(pressBack())
// }

@Test
fun testTrendingResultsThenSearchThenBackToTrending() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,25 @@ package com.burrowsapps.example.gif.ui.license
import android.Manifest.permission.INTERNET
import android.Manifest.permission.READ_EXTERNAL_STORAGE
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import android.content.Context
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withParent
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.compose.ui.test.onNodeWithText
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.GrantPermissionRule
import com.burrowsapps.example.gif.R
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.HiltTestApplication
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.containsString
import org.hamcrest.Matchers.instanceOf
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import test.ScreenshotWatcher
import javax.inject.Inject

@HiltAndroidTest
@Config(application = HiltTestApplication::class)
Expand All @@ -46,18 +43,16 @@ class LicenseActivityTest {
@get:Rule(order = 4)
val screenshotWatcher = ScreenshotWatcher()

@Inject @ApplicationContext internal lateinit var context: Context

@Before
fun setUp() {
hiltRule.inject()
}

@Test
fun testLicensesTitleIsShowing() {
onView(
allOf(
instanceOf(TextView::class.java),
withParent(instanceOf(Toolbar::class.java))
)
).check(matches(withText(containsString("Open source licenses"))))
fun testLicensesActivityTitleIsShowing() {
composeTestRule.onNodeWithText(context.getString(R.string.menu_licenses))
.assertIsDisplayed()
}
}
31 changes: 31 additions & 0 deletions app/src/main/java/com/burrowsapps/example/gif/data/ImageService.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,52 @@
package com.burrowsapps.example.gif.data

import android.content.Context
import android.graphics.drawable.Drawable
import android.widget.ImageView
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade
import com.bumptech.glide.load.resource.gif.GifDrawable
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.target.Target.SIZE_ORIGINAL
import com.bumptech.glide.request.transition.Transition
import com.burrowsapps.example.gif.di.GlideApp
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject

class ImageService @Inject constructor(@ApplicationContext private val context: Context) {

fun loadGif(
imageUrl: String,
thumbnailUrl: String,
onResourceReady: (GifDrawable?) -> Unit,
onLoadFailed: () -> Unit,
) {
loadGif(imageUrl)
.override(SIZE_ORIGINAL, SIZE_ORIGINAL)
.thumbnail(loadGif(thumbnailUrl))
.into(object : CustomTarget<GifDrawable>() {
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
onLoadFailed.invoke()
}

override fun onLoadCleared(placeholder: Drawable?) {
onLoadFailed.invoke()
}

override fun onResourceReady(
resource: GifDrawable,
transition: Transition<in GifDrawable>?,
) {
onResourceReady.invoke(resource)
}
})
}

fun loadGif(
imageUrl: String,
thumbnailUrl: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ class GifActivity : AppCompatActivity() {
(1.0 * Resources.getSystem().displayMetrics.density).roundToInt(), // TODO 1.dp in compose
gridLayoutManager.spanCount
)
gifAdapter = GifAdapter(onItemClick = { imageInfoModel ->
gifAdapter = GifAdapter(imageService) { imageInfoModel ->
showImageDialog(imageInfoModel)
}, imageService)
}

// Setup RecyclerView
activityBinding.recyclerView.apply {
Expand All @@ -76,12 +76,6 @@ class GifActivity : AppCompatActivity() {
setHasFixedSize(true)
setItemViewCacheSize(DEFAULT_LIMIT_COUNT) // default 2
recycledViewPool.setMaxRecycledViews(0, PORTRAIT_COLUMNS * 2) // default 5
addRecyclerListener { holder ->
val gifViewHolder = holder as GifAdapter.ViewHolder
GlideApp.with(this).clear(gifViewHolder.listBinding.gifImage)

Timber.i("addRecyclerListener")
}
addOnScrollListener(
object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
Expand Down
Loading

0 comments on commit 5690523

Please sign in to comment.