Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert Glide's Gallery sample to Compose. #4899

Merged
merged 1 commit into from Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions samples/gallery/build.gradle
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
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())
}
}
}
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.