diff options
8 files changed, 203 insertions, 26 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index 820fa3b2..c3b13527 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -307,7 +307,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements getCoroutineScope(getLifecycle()), previewViewModel.createOrReuseProvider(mChooserRequest.getTargetIntent()), mChooserRequest.getTargetIntent(), - previewViewModel.createOrReuseImageLoader(), + previewViewModel.getImageLoader(), createChooserActionFactory(), mEnterTransitionAnimationDelegate, new HeadlineGeneratorImpl(this)); diff --git a/java/src/com/android/intentresolver/contentpreview/BasePreviewViewModel.kt b/java/src/com/android/intentresolver/contentpreview/BasePreviewViewModel.kt index 4c781a46..3b20a45c 100644 --- a/java/src/com/android/intentresolver/contentpreview/BasePreviewViewModel.kt +++ b/java/src/com/android/intentresolver/contentpreview/BasePreviewViewModel.kt @@ -24,5 +24,7 @@ import androidx.lifecycle.ViewModel abstract class BasePreviewViewModel : ViewModel() { @MainThread abstract fun createOrReuseProvider(targetIntent: Intent): PreviewDataProvider - @MainThread abstract fun createOrReuseImageLoader(): ImageLoader + abstract val imageLoader: ImageLoader + + abstract val payloadToggleInteractor: PayloadToggleInteractor? } diff --git a/java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt b/java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt new file mode 100644 index 00000000..0e6d3869 --- /dev/null +++ b/java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver.contentpreview + +import android.net.Uri +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.update + +class PayloadToggleInteractor { + + private val storage = MutableStateFlow<Map<Any, Item>>(emptyMap()) // TODO: implement + private val selectedKeys = MutableStateFlow<Set<Any>>(emptySet()) + + val targetPosition: Flow<Int> = flowOf(0) // TODO: implement + val previewKeys: Flow<List<Any>> = flowOf(emptyList()) // TODO: implement + + fun setSelected(key: Any, isSelected: Boolean) { + if (isSelected) { + selectedKeys.update { it + key } + } else { + selectedKeys.update { it - key } + } + } + + fun selected(key: Any): Flow<Boolean> = previewKeys.map { key in it } + + fun previewInteractor(key: Any) = PayloadTogglePreviewInteractor(key, this) + + fun previewUri(key: Any): Flow<Uri?> = storage.map { it[key]?.previewUri } + + private data class Item( + val previewUri: Uri?, + ) +} + +class PayloadTogglePreviewInteractor( + private val key: Any, + private val interactor: PayloadToggleInteractor, +) { + fun setSelected(selected: Boolean) { + interactor.setSelected(key, selected) + } + + val previewUri: Flow<Uri?> = interactor.previewUri(key) + val selected: Flow<Boolean> = interactor.selected(key) +} diff --git a/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt b/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt index 9acc4689..d855ea16 100644 --- a/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt +++ b/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt @@ -41,7 +41,6 @@ constructor( @Background private val dispatcher: CoroutineDispatcher = Dispatchers.IO, ) : BasePreviewViewModel() { private var previewDataProvider: PreviewDataProvider? = null - private var imageLoader: ImagePreviewImageLoader? = null @MainThread override fun createOrReuseProvider(targetIntent: Intent): PreviewDataProvider = @@ -53,19 +52,21 @@ constructor( ) .also { previewDataProvider = it } - @MainThread - override fun createOrReuseImageLoader(): ImageLoader = - imageLoader - ?: ImagePreviewImageLoader( - viewModelScope + dispatcher, - thumbnailSize = - application.resources.getDimensionPixelSize( - R.dimen.chooser_preview_image_max_dimen - ), - application.contentResolver, - cacheSize = 16 - ) - .also { imageLoader = it } + override val imageLoader by lazy { + ImagePreviewImageLoader( + viewModelScope + dispatcher, + thumbnailSize = + application.resources.getDimensionPixelSize( + R.dimen.chooser_preview_image_max_dimen + ), + application.contentResolver, + cacheSize = 16 + ) + } + + override val payloadToggleInteractor: PayloadToggleInteractor? by lazy { + PayloadToggleInteractor() + } companion object { val Factory: ViewModelProvider.Factory = diff --git a/java/src/com/android/intentresolver/contentpreview/ShareouselContentPreviewUi.kt b/java/src/com/android/intentresolver/contentpreview/ShareouselContentPreviewUi.kt new file mode 100644 index 00000000..a10d3272 --- /dev/null +++ b/java/src/com/android/intentresolver/contentpreview/ShareouselContentPreviewUi.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.intentresolver.contentpreview + +import android.content.res.Resources +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.platform.ComposeView +import androidx.lifecycle.viewmodel.compose.viewModel +import com.android.intentresolver.R +import com.android.intentresolver.contentpreview.ChooserContentPreviewUi.ActionFactory +import com.android.intentresolver.contentpreview.shareousel.ui.composable.Shareousel +import com.android.intentresolver.contentpreview.shareousel.ui.viewmodel.toShareouselViewModel + +internal class ShareouselContentPreviewUi( + private val actionFactory: ActionFactory, +) : ContentPreviewUi() { + + override fun getType(): Int = ContentPreviewType.CONTENT_PREVIEW_IMAGE + + override fun display( + resources: Resources, + layoutInflater: LayoutInflater, + parent: ViewGroup, + headlineViewParent: View?, + ): ViewGroup { + return displayInternal(parent, headlineViewParent).also { layout -> + displayModifyShareAction(headlineViewParent ?: layout, actionFactory) + } + } + + private fun displayInternal( + parent: ViewGroup, + headlineViewParent: View?, + ): ViewGroup { + if (headlineViewParent != null) { + inflateHeadline(headlineViewParent) + } + val composeView = + ComposeView(parent.context).apply { + setContent { + val vm: BasePreviewViewModel = viewModel() + val interactor = + requireNotNull(vm.payloadToggleInteractor) { "Should not be null" } + val viewModel = interactor.toShareouselViewModel(vm.imageLoader) + + if (headlineViewParent != null) { + LaunchedEffect(Unit) { + viewModel.headline.collect { headline -> + headlineViewParent.findViewById<TextView>(R.id.headline)?.apply { + if (headline.isNotBlank()) { + text = headline + visibility = View.VISIBLE + } else { + visibility = View.GONE + } + } + } + } + } + + Shareousel(viewModel = viewModel) + } + } + return composeView + } +} diff --git a/java/src/com/android/intentresolver/contentpreview/shareousel/ui/viewmodel/ShareouselViewModel.kt b/java/src/com/android/intentresolver/contentpreview/shareousel/ui/viewmodel/ShareouselViewModel.kt index 39f2040b..4592ea6d 100644 --- a/java/src/com/android/intentresolver/contentpreview/shareousel/ui/viewmodel/ShareouselViewModel.kt +++ b/java/src/com/android/intentresolver/contentpreview/shareousel/ui/viewmodel/ShareouselViewModel.kt @@ -16,8 +16,12 @@ package com.android.intentresolver.contentpreview.shareousel.ui.viewmodel import android.graphics.Bitmap +import com.android.intentresolver.contentpreview.ImageLoader +import com.android.intentresolver.contentpreview.PayloadToggleInteractor import com.android.intentresolver.icon.ComposeIcon import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.map data class ShareouselViewModel( val headline: Flow<String>, @@ -36,3 +40,22 @@ data class ShareouselImageViewModel( val setSelected: (Boolean) -> Unit, val onActionClick: () -> Unit, ) + +fun PayloadToggleInteractor.toShareouselViewModel(imageLoader: ImageLoader): ShareouselViewModel { + return ShareouselViewModel( + headline = MutableStateFlow("Shareousel"), + previewKeys = previewKeys, + actions = MutableStateFlow(emptyList()), + centerIndex = targetPosition, + previewForKey = { key -> + val previewInteractor = previewInteractor(key) + ShareouselImageViewModel( + bitmap = previewInteractor.previewUri.map { uri -> uri?.let { imageLoader(uri) } }, + contentDescription = MutableStateFlow(""), + isSelected = previewInteractor.selected, + setSelected = { isSelected -> previewInteractor.setSelected(isSelected) }, + onActionClick = {}, + ) + } + ) +} diff --git a/java/src/com/android/intentresolver/v2/ChooserActivity.java b/java/src/com/android/intentresolver/v2/ChooserActivity.java index 64c5881c..b9cf53b6 100644 --- a/java/src/com/android/intentresolver/v2/ChooserActivity.java +++ b/java/src/com/android/intentresolver/v2/ChooserActivity.java @@ -474,7 +474,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements getCoroutineScope(getLifecycle()), previewViewModel.createOrReuseProvider(chooserRequest.getTargetIntent()), chooserRequest.getTargetIntent(), - previewViewModel.createOrReuseImageLoader(), + previewViewModel.getImageLoader(), createChooserActionFactory(), mEnterTransitionAnimationDelegate, new HeadlineGeneratorImpl(this)); diff --git a/tests/shared/src/com/android/intentresolver/TestContentPreviewViewModel.kt b/tests/shared/src/com/android/intentresolver/TestContentPreviewViewModel.kt index 888fc161..998c0802 100644 --- a/tests/shared/src/com/android/intentresolver/TestContentPreviewViewModel.kt +++ b/tests/shared/src/com/android/intentresolver/TestContentPreviewViewModel.kt @@ -22,19 +22,22 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.CreationExtras import com.android.intentresolver.contentpreview.BasePreviewViewModel import com.android.intentresolver.contentpreview.ImageLoader +import com.android.intentresolver.contentpreview.PayloadToggleInteractor import com.android.intentresolver.contentpreview.PreviewDataProvider /** A test content preview model that supports image loader override. */ class TestContentPreviewViewModel( private val viewModel: BasePreviewViewModel, - private val imageLoader: ImageLoader? = null, + private val imageLoaderDelegate: ImageLoader?, ) : BasePreviewViewModel() { - override fun createOrReuseProvider( - targetIntent: Intent - ): PreviewDataProvider = viewModel.createOrReuseProvider(targetIntent) + override fun createOrReuseProvider(targetIntent: Intent): PreviewDataProvider = + viewModel.createOrReuseProvider(targetIntent) - override fun createOrReuseImageLoader(): ImageLoader = - imageLoader ?: viewModel.createOrReuseImageLoader() + override val imageLoader: ImageLoader + get() = imageLoaderDelegate ?: viewModel.imageLoader + + override val payloadToggleInteractor: PayloadToggleInteractor? + get() = viewModel.payloadToggleInteractor companion object { fun wrap( @@ -47,10 +50,12 @@ class TestContentPreviewViewModel( modelClass: Class<T>, extras: CreationExtras ): T { + val wrapped = factory.create(modelClass, extras) as BasePreviewViewModel return TestContentPreviewViewModel( - factory.create(modelClass, extras) as BasePreviewViewModel, - imageLoader, - ) as T + wrapped, + imageLoader ?: wrapped.imageLoader, + ) + as T } } } |