diff options
| author | 2024-05-24 18:27:16 -0400 | |
|---|---|---|
| committer | 2024-05-29 09:14:09 -0400 | |
| commit | 971f4321406cc7ae4346f9ecbb220a0095a4151f (patch) | |
| tree | 425574a53f1e2b5e65f0e6ed5fcc42c093a1d57e /java | |
| parent | a5734e47268d2b02e993580a022c4aa0be3a351a (diff) | |
Cursor continuously fetches new pages as the user scrolls
Whenever the user gets more than half a page away from the center, the
cursor will fetch the next page in that direction.
Test: manual test with Share Test
Test: atest IntentResolver-tests-unit
BUG: 341923886
FIX: 341923886
Flag: android.service.chooser.chooser_payload_toggling
Change-Id: Ifc9f651ccf028f25af8adfb7bc359977803d540a
Diffstat (limited to 'java')
6 files changed, 60 insertions, 7 deletions
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractor.kt index a0fc11c3..fa600c86 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractor.kt @@ -43,6 +43,8 @@ import dagger.hilt.components.SingletonComponent import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject import javax.inject.Qualifier +import kotlin.math.max +import kotlin.math.min import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filterNotNull @@ -93,11 +95,14 @@ constructor( var state = initialState val startPageNum = state.firstLoadedPageNum while ((state.hasMoreLeft || state.hasMoreRight) && state.numLoadedPages < maxLoadedPages) { + val (leftTriggerIndex, rightTriggerIndex) = state.triggerIndices() interactor.setPreviews( previews = state.merged.values.toList(), startIndex = startPageNum, hasMoreLeft = state.hasMoreLeft, hasMoreRight = state.hasMoreRight, + leftTriggerIndex = leftTriggerIndex, + rightTriggerIndex = rightTriggerIndex, ) val loadedLeft = startPageNum - state.firstLoadedPageNum val loadedRight = state.lastLoadedPageNum - startPageNum @@ -120,6 +125,8 @@ constructor( ) { var state = initialState while (true) { + val (leftTriggerIndex, rightTriggerIndex) = state.triggerIndices() + // Design note: in order to prevent load requests from the UI when it was displaying a // previously-published dataset being accidentally associated with a recently-published // one, we generate a new Flow of load requests for each dataset and only listen to @@ -130,6 +137,8 @@ constructor( startIndex = 0, // TODO: actually track this as the window changes? hasMoreLeft = state.hasMoreLeft, hasMoreRight = state.hasMoreRight, + leftTriggerIndex = leftTriggerIndex, + rightTriggerIndex = rightTriggerIndex, ) state = loadingState.handleOneLoadRequest(state, pagedCursor, unclaimedRecords) } @@ -238,6 +247,13 @@ constructor( } } + private fun CursorWindow.triggerIndices(): Pair<Int, Int> { + val totalIndices = numLoadedPages * pageSize + val midIndex = totalIndices / 2 + val halfPage = pageSize / 2 + return max(midIndex - halfPage, 0) to min(midIndex + halfPage, totalIndices - 1) + } + private suspend fun readPage( state: CursorWindow, pagedCursor: PagedCursor<CursorRow?>, diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractor.kt index 388cbc7e..c9c9a9b3 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractor.kt @@ -51,6 +51,8 @@ constructor( startIndex = focusedItemIdx, hasMoreLeft = false, hasMoreRight = false, + leftTriggerIndex = initialPreviewMap.indices.first(), + rightTriggerIndex = initialPreviewMap.indices.last(), ) cursorInteractor.launch(cursor.await() ?: return@coroutineScope, initialPreviewMap) } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SetCursorPreviewsInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SetCursorPreviewsInteractor.kt index 437bc942..124e2a3d 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SetCursorPreviewsInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SetCursorPreviewsInteractor.kt @@ -35,6 +35,8 @@ constructor(private val previewsRepo: CursorPreviewsRepository) { startIndex: Int, hasMoreLeft: Boolean, hasMoreRight: Boolean, + leftTriggerIndex: Int, + rightTriggerIndex: Int ): Flow<LoadDirection?> { val loadingState = MutableStateFlow<LoadDirection?>(null) previewsRepo.previewsModel.value = @@ -53,6 +55,8 @@ constructor(private val previewsRepo: CursorPreviewsRepository) { } else { null }, + leftTriggerIndex = leftTriggerIndex, + rightTriggerIndex = rightTriggerIndex, ) return loadingState.asStateFlow() } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/model/PreviewsModel.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/model/PreviewsModel.kt index 1d3eb4b4..ae8bd1eb 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/model/PreviewsModel.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/model/PreviewsModel.kt @@ -32,4 +32,14 @@ data class PreviewsModel( * indicates that there is no more data to load in that direction. */ val loadMoreRight: (() -> Unit)?, + /** + * Index into [previewModels] where any attempted access less than or equal to it should trigger + * a window shift left. + */ + val leftTriggerIndex: Int, + /** + * Index into [previewModels] where any attempted access greater than or equal to it should + * trigger a window shift right. + */ + val rightTriggerIndex: Int, ) diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselComposable.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselComposable.kt index 02d997ae..8e2626bf 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselComposable.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselComposable.kt @@ -41,7 +41,9 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -59,6 +61,7 @@ import com.android.intentresolver.contentpreview.payloadtoggle.shared.ContentTyp import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewsModel import com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel.ShareouselPreviewViewModel import com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel.ShareouselViewModel +import kotlin.math.abs import kotlinx.coroutines.launch @Composable @@ -104,7 +107,24 @@ private fun PreviewCarousel( .systemGestureExclusion() ) { itemsIndexed(previews.previewModels, key = { _, model -> model.uri }) { index, model -> - ShareouselCard(viewModel.preview(index, model)) + + // Index if this is the element in the center of the viewing area, otherwise null + val previewIndex by remember { + derivedStateOf { + carouselState.layoutInfo.visibleItemsInfo + .firstOrNull { it.index == index } + ?.let { + val viewportCenter = carouselState.layoutInfo.viewportEndOffset / 2 + val halfPreviewWidth = it.size / 2 + val previewCenter = it.offset + halfPreviewWidth + val previewDistanceToViewportCenter = + abs(previewCenter - viewportCenter) + if (previewDistanceToViewportCenter <= halfPreviewWidth) index else null + } + } + } + + ShareouselCard(viewModel.preview(model, previewIndex)) } } } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModel.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModel.kt index 9d53b92a..c3ad7b6c 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModel.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModel.kt @@ -55,7 +55,7 @@ data class ShareouselViewModel( /** List of action chips presented underneath Shareousel. */ val actions: Flow<List<ActionChipViewModel>>, /** Creates a [ShareouselPreviewViewModel] for a [PreviewModel] present in [previews]. */ - val preview: (index: Int, key: PreviewModel) -> ShareouselPreviewViewModel, + val preview: (key: PreviewModel, index: Int?) -> ShareouselPreviewViewModel, ) @Module @@ -112,7 +112,7 @@ interface ShareouselViewModelModule { } } }, - preview = { index, key -> + preview = { key, index -> keySet.value?.maybeLoad(index) val previewInteractor = interactor.preview(key) val contentType = @@ -134,9 +134,10 @@ interface ShareouselViewModelModule { } } -private fun PreviewsModel.maybeLoad(index: Int) { - when (index) { - previewModels.indices.firstOrNull() -> loadMoreLeft?.invoke() - previewModels.indices.lastOrNull() -> loadMoreRight?.invoke() +private fun PreviewsModel.maybeLoad(index: Int?) { + when { + index == null -> {} + index <= leftTriggerIndex -> loadMoreLeft?.invoke() + index >= rightTriggerIndex -> loadMoreRight?.invoke() } } |