Skip to content

Commit

Permalink
Convert Glide's Gallery sample to Compose.
Browse files Browse the repository at this point in the history
  • Loading branch information
sjudd committed Sep 23, 2022
1 parent 5366e7b commit 272c6c3
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 157 deletions.
14 changes: 12 additions & 2 deletions samples/gallery/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ apply plugin: 'com.google.devtools.ksp'

dependencies {
implementation project(':library')
implementation project(':integration:compose')
implementation(project(':integration:recyclerview')) {
transitive = false
}

implementation "androidx.recyclerview:recyclerview:$ANDROID_X_RECYCLERVIEW_VERSION"
implementation "androidx.fragment:fragment-ktx:$ANDROID_X_FRAGMENT_VERSION"
implementation "androidx.core:core-ktx:$ANDROID_X_CORE_KTX_VERSION"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$ANDROID_X_LIFECYCLE_KTX_VERSION"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$ANDROID_X_LIFECYCLE_KTX_VERSION"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$JETBRAINS_KOTLINX_COROUTINES_VERSION"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$JETBRAINS_KOTLINX_COROUTINES_VERSION"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$JETBRAINS_KOTLIN_VERSION"
implementation "androidx.compose.foundation:foundation:$ANDROID_X_COMPOSE_VERSION"
implementation "androidx.compose.ui:ui:$ANDROID_X_COMPOSE_VERSION"

ksp project(':annotation:ksp')
}
Expand All @@ -37,6 +38,15 @@ android {
versionCode 1
versionName '1.0'
}
buildFeatures {
compose = true
}
kotlinOptions {
jvmTarget = "11"
}
composeOptions {
kotlinCompilerExtensionVersion '1.2.0'
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_11
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,88 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.unit.dp
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.GridLayoutManager
import com.bumptech.glide.Glide
import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader
import kotlinx.coroutines.launch
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
import com.bumptech.glide.integration.compose.GlideImage
import com.bumptech.glide.integration.compose.GlideLazyListPreloader
import com.bumptech.glide.signature.MediaStoreSignature

/** Displays media store data in a recycler view. */
@OptIn(ExperimentalGlideComposeApi::class)
class HorizontalGalleryFragment : Fragment() {
private lateinit var adapter: RecyclerAdapter
private lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
): View {
val galleryViewModel: GalleryViewModel by viewModels()
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
galleryViewModel.mediaStoreData.collect { data ->
adapter.setData(data)
}
return ComposeView(requireContext()).apply {
setContent {
LoadableDeviceMedia(galleryViewModel)
}
}
}

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
): View? {
val result = inflater.inflate(R.layout.recycler_view, container, false)
recyclerView = result.findViewById<View>(R.id.recycler_view) as RecyclerView
val layoutManager = GridLayoutManager(activity, 1)
layoutManager.orientation = RecyclerView.HORIZONTAL
recyclerView.layoutManager = layoutManager
recyclerView.setHasFixedSize(true)

val glideRequests = Glide.with(this)
adapter = RecyclerAdapter(requireContext(), glideRequests)
val preloader = RecyclerViewPreloader(glideRequests, adapter, adapter, 3)
recyclerView.addOnScrollListener(preloader)
recyclerView.adapter = adapter
return result
@Composable
fun LoadableDeviceMedia(viewModel: GalleryViewModel) {
val mediaStoreData = viewModel.mediaStoreData.collectAsState()
DeviceMedia(mediaStoreData.value)
}

@Composable
fun DeviceMedia(mediaStoreData: List<MediaStoreData>) {
val state = rememberLazyListState()
LazyRow(horizontalArrangement = Arrangement.spacedBy(10.dp), state = state) {
items(mediaStoreData) { mediaStoreItem ->
MediaStoreView(mediaStoreItem, Modifier.fillParentMaxSize())
}
}

GlideLazyListPreloader(
state = state,
data = mediaStoreData,
size = THUMBNAIL_SIZE,
numberOfItemsToPreload = 15,
fixedVisibleItemCount = 2,
) { item, requestBuilder -> requestBuilder.load(item.uri).signature(item.signature()) }
}

private fun MediaStoreData.signature() =
MediaStoreSignature(mimeType, dateModified, orientation)

@Composable
fun MediaStoreView(item: MediaStoreData, modifier: Modifier) {
val requestManager = Glide.with(requireContext())
val signature = item.signature()

GlideImage(
model = item.uri,
contentDescription = item.displayName,
modifier = modifier,
) {
it.thumbnail(
requestManager.asDrawable()
.load(item.uri)
.signature(signature)
.override(THUMBNAIL_DIMENSION)
)
.signature(signature)
}
}

companion object {
private const val THUMBNAIL_DIMENSION = 50
private val THUMBNAIL_SIZE = Size(THUMBNAIL_DIMENSION.toFloat(), THUMBNAIL_DIMENSION.toFloat())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,15 @@ class MediaStoreDataSource internal constructor(
val mimeTypeColNum = cursor.getColumnIndexOrThrow(MediaColumns.MIME_TYPE)
val orientationColNum = cursor.getColumnIndexOrThrow(MediaColumns.ORIENTATION)
val mediaTypeColumnIndex = cursor.getColumnIndexOrThrow(FileColumns.MEDIA_TYPE)
val displayNameIndex = cursor.getColumnIndexOrThrow(FileColumns.DISPLAY_NAME)

while (cursor.moveToNext()) {
val id = cursor.getLong(idColNum)
val dateTaken = cursor.getLong(dateTakenColNum)
val mimeType = cursor.getString(mimeTypeColNum)
val dateModified = cursor.getLong(dateModifiedColNum)
val orientation = cursor.getInt(orientationColNum)
val displayName = cursor.getString(displayNameIndex)
val type =
if (cursor.getInt(mediaTypeColumnIndex) == FileColumns.MEDIA_TYPE_IMAGE)
Type.IMAGE
Expand All @@ -89,7 +91,9 @@ class MediaStoreDataSource internal constructor(
mimeType = mimeType,
dateModified = dateModified,
orientation = orientation,
dateTaken = dateTaken))
dateTaken = dateTaken,
displayName = displayName,
))
}
}
return data
Expand All @@ -103,6 +107,7 @@ class MediaStoreDataSource internal constructor(
MediaColumns.DATE_MODIFIED,
MediaColumns.MIME_TYPE,
MediaColumns.ORIENTATION,
MediaColumns.DISPLAY_NAME,
FileColumns.MEDIA_TYPE)
}
}
Expand All @@ -117,6 +122,7 @@ data class MediaStoreData(
val dateModified: Long,
val orientation: Int,
val dateTaken: Long,
val displayName: String?
) : Parcelable

/** The type of data. */
Expand Down

This file was deleted.

0 comments on commit 272c6c3

Please sign in to comment.