diff options
author | 2024-06-20 16:30:13 -0700 | |
---|---|---|
committer | 2024-07-09 13:56:54 -0700 | |
commit | 864d466559a5e576bbf02ba96b2850a8a30291e8 (patch) | |
tree | 1dfa76b0a50d59809fa758b7f7145f777916c598 | |
parent | d686ba409ff985e2715589b4beba4c7e19b58fc9 (diff) |
Delete PreviewViewModel
Merge PreviewViewModel functionality into ChooserViewModel.
Make ImageLoader injectable; remove obsolete code.
Bug: 348665058
Test: atest IntentResolver-tests-activity
Test: atest IntentResolver-tests-unit
Flag: EXEMPT refactor
Change-Id: Icb18362763c19e97ac39cfa8112c9d0357f24e1e
11 files changed, 37 insertions, 251 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index 0fa5e758..4d70f550 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -96,10 +96,8 @@ import com.android.intentresolver.ChooserRefinementManager.RefinementType; import com.android.intentresolver.chooser.DisplayResolveInfo; import com.android.intentresolver.chooser.MultiDisplayResolveInfo; import com.android.intentresolver.chooser.TargetInfo; -import com.android.intentresolver.contentpreview.BasePreviewViewModel; import com.android.intentresolver.contentpreview.ChooserContentPreviewUi; import com.android.intentresolver.contentpreview.HeadlineGeneratorImpl; -import com.android.intentresolver.contentpreview.PreviewViewModel; import com.android.intentresolver.data.model.ChooserRequest; import com.android.intentresolver.data.repository.DevicePolicyResources; import com.android.intentresolver.domain.interactor.UserInteractor; @@ -633,21 +631,14 @@ public class ChooserActivity extends Hilt_ChooserActivity implements finish(); } }); - BasePreviewViewModel previewViewModel = - new ViewModelProvider(this, createPreviewViewModelFactory()) - .get(BasePreviewViewModel.class); - previewViewModel.init( - mRequest.getTargetIntent(), - mRequest.getAdditionalContentUri(), - mChooserServiceFeatureFlags.chooserPayloadToggling()); ChooserContentPreviewUi.ActionFactory actionFactory = decorateActionFactoryWithRefinement( createChooserActionFactory(mRequest.getTargetIntent())); mChooserContentPreviewUi = new ChooserContentPreviewUi( getCoroutineScope(getLifecycle()), - previewViewModel.getPreviewDataProvider(), + mViewModel.getPreviewDataProvider(), mRequest.getTargetIntent(), - previewViewModel.getImageLoader(), + mViewModel.getImageLoader(), actionFactory, createModifyShareActionFactory(), mEnterTransitionAnimationDelegate, @@ -2108,11 +2099,6 @@ public class ChooserActivity extends Hilt_ChooserActivity implements mPinnedSharedPrefs); } - @VisibleForTesting - protected ViewModelProvider.Factory createPreviewViewModelFactory() { - return PreviewViewModel.Companion.getFactory(); - } - private ChooserContentPreviewUi.ActionFactory decorateActionFactoryWithRefinement( ChooserContentPreviewUi.ActionFactory originalFactory) { if (!mFeatureFlags.refineSystemActions()) { diff --git a/java/src/com/android/intentresolver/contentpreview/BasePreviewViewModel.kt b/java/src/com/android/intentresolver/contentpreview/BasePreviewViewModel.kt deleted file mode 100644 index dc36e584..00000000 --- a/java/src/com/android/intentresolver/contentpreview/BasePreviewViewModel.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 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.Intent -import android.net.Uri -import androidx.annotation.MainThread -import androidx.lifecycle.ViewModel - -/** A contract for the preview view model. Added for testing. */ -abstract class BasePreviewViewModel : ViewModel() { - @get:MainThread abstract val previewDataProvider: PreviewDataProvider - @get:MainThread abstract val imageLoader: ImageLoader - - @MainThread - abstract fun init( - targetIntent: Intent, - additionalContentUri: Uri?, - isPayloadTogglingEnabled: Boolean, - ) -} diff --git a/java/src/com/android/intentresolver/contentpreview/ImageLoaderModule.kt b/java/src/com/android/intentresolver/contentpreview/ImageLoaderModule.kt index 7035f765..17d05099 100644 --- a/java/src/com/android/intentresolver/contentpreview/ImageLoaderModule.kt +++ b/java/src/com/android/intentresolver/contentpreview/ImageLoaderModule.kt @@ -23,19 +23,14 @@ import dagger.Binds import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.components.ActivityRetainedComponent -import dagger.hilt.android.scopes.ActivityRetainedScoped +import dagger.hilt.android.components.ViewModelComponent @Module -@InstallIn(ActivityRetainedComponent::class) +@InstallIn(ViewModelComponent::class) interface ImageLoaderModule { - @Binds - @ActivityRetainedScoped - fun imageLoader(previewImageLoader: ImagePreviewImageLoader): ImageLoader + @Binds fun imageLoader(previewImageLoader: ImagePreviewImageLoader): ImageLoader - @Binds - @ActivityRetainedScoped - fun thumbnailLoader(thumbnailLoader: ThumbnailLoaderImpl): ThumbnailLoader + @Binds fun thumbnailLoader(thumbnailLoader: ThumbnailLoaderImpl): ThumbnailLoader companion object { @Provides diff --git a/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt b/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt index 96bb8258..9b2dbebf 100644 --- a/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt +++ b/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt @@ -32,6 +32,7 @@ import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREV import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_IMAGE import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_TEXT +import com.android.intentresolver.inject.ChooserServiceFlags import com.android.intentresolver.measurements.runTracing import com.android.intentresolver.util.ownedByCurrentUser import java.util.concurrent.atomic.AtomicInteger @@ -76,9 +77,7 @@ constructor( private val targetIntent: Intent, private val additionalContentUri: Uri?, private val contentResolver: ContentInterface, - // TODO: replace with the ChooserServiceFlags ref when PreviewViewModel dependencies are sorted - // out - private val isPayloadTogglingEnabled: Boolean, + private val featureFlags: ChooserServiceFlags, private val typeClassifier: MimeTypeClassifier = DefaultMimeTypeClassifier, ) { @@ -129,7 +128,7 @@ constructor( * IMAGE, FILE, TEXT. */ if (!targetIntent.isSend || records.isEmpty()) { CONTENT_PREVIEW_TEXT - } else if (isPayloadTogglingEnabled && shouldShowPayloadSelection()) { + } else if (featureFlags.chooserPayloadToggling() && shouldShowPayloadSelection()) { // TODO: replace with the proper flags injection CONTENT_PREVIEW_PAYLOAD_SELECTION } else { @@ -275,13 +274,16 @@ constructor( val mimeType: String? by lazy { contentResolver.getTypeSafe(uri) } val isImageType: Boolean get() = typeClassifier.isImageType(mimeType) + val supportsImageType: Boolean by lazy { contentResolver.getStreamTypesSafe(uri).firstOrNull(typeClassifier::isImageType) != null } val supportsThumbnail: Boolean get() = query.supportsThumbnail + val title: String get() = query.title + val iconUri: Uri? get() = query.iconUri @@ -326,8 +328,7 @@ constructor( } QueryResult(supportsThumbnail, title, iconUri) - } - ?: QueryResult() + } ?: QueryResult() } private class QueryResult( diff --git a/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt b/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt deleted file mode 100644 index 6a729945..00000000 --- a/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2023 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.app.Application -import android.content.ContentResolver -import android.content.Intent -import android.net.Uri -import androidx.annotation.MainThread -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY -import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.CreationExtras -import com.android.intentresolver.R -import com.android.intentresolver.inject.Background -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.plus - -/** A view model for the preview logic */ -class PreviewViewModel( - private val contentResolver: ContentResolver, - // TODO: inject ImageLoader instead - private val thumbnailSize: Int, - @Background private val dispatcher: CoroutineDispatcher = Dispatchers.IO, -) : BasePreviewViewModel() { - private var targetIntent: Intent? = null - private var additionalContentUri: Uri? = null - private var isPayloadTogglingEnabled = false - - override val previewDataProvider by lazy { - val targetIntent = requireNotNull(this.targetIntent) { "Not initialized" } - PreviewDataProvider( - viewModelScope + dispatcher, - targetIntent, - additionalContentUri, - contentResolver, - isPayloadTogglingEnabled, - ) - } - - override val imageLoader by lazy { - ImagePreviewImageLoader( - viewModelScope + dispatcher, - thumbnailSize, - contentResolver, - cacheSize = 16 - ) - } - - // TODO: make the view model injectable and inject these dependencies instead - @MainThread - override fun init( - targetIntent: Intent, - additionalContentUri: Uri?, - isPayloadTogglingEnabled: Boolean, - ) { - if (this.targetIntent != null) return - this.targetIntent = targetIntent - this.additionalContentUri = additionalContentUri - this.isPayloadTogglingEnabled = isPayloadTogglingEnabled - } - - companion object { - val Factory: ViewModelProvider.Factory = - object : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun <T : ViewModel> create( - modelClass: Class<T>, - extras: CreationExtras - ): T { - val application: Application = checkNotNull(extras[APPLICATION_KEY]) - return PreviewViewModel( - application.contentResolver, - application.resources.getDimensionPixelSize( - R.dimen.chooser_preview_image_max_dimen - ) - ) - as T - } - } - } -} diff --git a/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt b/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt index c9cae3db..619e118a 100644 --- a/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt +++ b/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt @@ -15,10 +15,13 @@ */ package com.android.intentresolver.ui.viewmodel +import android.content.ContentInterface import android.util.Log import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.android.intentresolver.contentpreview.ImageLoader +import com.android.intentresolver.contentpreview.PreviewDataProvider import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.FetchPreviewsInteractor import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.ProcessTargetIntentUpdatesInteractor import com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel.ShareouselViewModel @@ -38,6 +41,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.plus private const val TAG = "ChooserViewModel" @@ -58,6 +62,8 @@ constructor( */ val initialRequest: ValidationResult<ChooserRequest>, private val chooserRequestRepository: Lazy<ChooserRequestRepository>, + private val contentResolver: ContentInterface, + val imageLoader: ImageLoader, ) : ViewModel() { /** Parcelable-only references provided from the creating Activity */ @@ -86,6 +92,17 @@ constructor( val request: StateFlow<ChooserRequest> get() = chooserRequestRepository.get().chooserRequest.asStateFlow() + val previewDataProvider by lazy { + val chooserRequest = (initialRequest as Valid<ChooserRequest>).value + PreviewDataProvider( + viewModelScope + bgDispatcher, + chooserRequest.targetIntent, + chooserRequest.additionalContentUri, + contentResolver, + flags, + ) + } + init { if (initialRequest is Invalid) { Log.w(TAG, "initialRequest is Invalid, initialization failed") diff --git a/tests/activity/src/com/android/intentresolver/ChooserActivityOverrideData.java b/tests/activity/src/com/android/intentresolver/ChooserActivityOverrideData.java index 507ce3d7..311201cf 100644 --- a/tests/activity/src/com/android/intentresolver/ChooserActivityOverrideData.java +++ b/tests/activity/src/com/android/intentresolver/ChooserActivityOverrideData.java @@ -26,7 +26,6 @@ import android.database.Cursor; import android.os.UserHandle; import com.android.intentresolver.chooser.TargetInfo; -import com.android.intentresolver.contentpreview.ImageLoader; import com.android.intentresolver.emptystate.CrossProfileIntentsChecker; import com.android.intentresolver.shortcuts.ShortcutLoader; @@ -58,7 +57,6 @@ public class ChooserActivityOverrideData { public Boolean isVoiceInteraction; public Cursor resolverCursor; public boolean resolverForceException; - public ImageLoader imageLoader; public Resources resources; public boolean hasCrossProfileIntents; public boolean isQuietModeEnabled; @@ -68,7 +66,6 @@ public class ChooserActivityOverrideData { public void reset() { onSafelyStartInternalCallback = null; isVoiceInteraction = null; - imageLoader = null; resolverCursor = null; resolverForceException = false; resolverListController = mock(ChooserListController.class); diff --git a/tests/activity/src/com/android/intentresolver/ChooserActivityTest.java b/tests/activity/src/com/android/intentresolver/ChooserActivityTest.java index a8b8b2e9..620ac555 100644 --- a/tests/activity/src/com/android/intentresolver/ChooserActivityTest.java +++ b/tests/activity/src/com/android/intentresolver/ChooserActivityTest.java @@ -305,9 +305,6 @@ public class ChooserActivityTest { // values to the dependency graph at activity launch time. This allows replacing // arbitrary bindings per-test case if needed. mPackageManager = mContext.getPackageManager(); - - // TODO: inject image loader in the prod code and remove this override - ChooserActivityOverrideData.getInstance().imageLoader = mFakeImageLoader; } public ChooserActivityTest(boolean appPredictionAvailable) { diff --git a/tests/activity/src/com/android/intentresolver/ChooserWrapperActivity.java b/tests/activity/src/com/android/intentresolver/ChooserWrapperActivity.java index 4b71aa29..8a8fe355 100644 --- a/tests/activity/src/com/android/intentresolver/ChooserWrapperActivity.java +++ b/tests/activity/src/com/android/intentresolver/ChooserWrapperActivity.java @@ -30,8 +30,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; -import androidx.lifecycle.ViewModelProvider; - import com.android.intentresolver.chooser.DisplayResolveInfo; import com.android.intentresolver.chooser.TargetInfo; import com.android.intentresolver.emptystate.CrossProfileIntentsChecker; @@ -152,13 +150,6 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW } @Override - protected ViewModelProvider.Factory createPreviewViewModelFactory() { - return TestContentPreviewViewModel.Companion.wrap( - super.createPreviewViewModelFactory(), - sOverrides.imageLoader); - } - - @Override public Cursor queryResolver(ContentResolver resolver, Uri uri) { if (sOverrides.resolverCursor != null) { return sOverrides.resolverCursor; diff --git a/tests/shared/src/com/android/intentresolver/TestContentPreviewViewModel.kt b/tests/shared/src/com/android/intentresolver/TestContentPreviewViewModel.kt deleted file mode 100644 index 8f246424..00000000 --- a/tests/shared/src/com/android/intentresolver/TestContentPreviewViewModel.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2023 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 - -import android.content.Intent -import android.net.Uri -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.viewmodel.CreationExtras -import com.android.intentresolver.contentpreview.BasePreviewViewModel -import com.android.intentresolver.contentpreview.ImageLoader - -/** A test content preview model that supports image loader override. */ -class TestContentPreviewViewModel( - private val viewModel: BasePreviewViewModel, - override val imageLoader: ImageLoader, -) : BasePreviewViewModel() { - - override val previewDataProvider - get() = viewModel.previewDataProvider - - override fun init( - targetIntent: Intent, - additionalContentUri: Uri?, - isPayloadTogglingEnabled: Boolean, - ) { - viewModel.init(targetIntent, additionalContentUri, isPayloadTogglingEnabled) - } - - companion object { - fun wrap( - factory: ViewModelProvider.Factory, - imageLoader: ImageLoader?, - ): ViewModelProvider.Factory = - object : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun <T : ViewModel> create( - modelClass: Class<T>, - extras: CreationExtras - ): T { - val wrapped = factory.create(modelClass, extras) as BasePreviewViewModel - return TestContentPreviewViewModel( - wrapped, - imageLoader ?: wrapped.imageLoader, - ) - as T - } - } - } -} diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt index a2fb9693..370ee044 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt @@ -21,9 +21,9 @@ import android.content.Intent import android.database.MatrixCursor import android.media.MediaMetadata import android.net.Uri -import android.platform.test.flag.junit.CheckFlagsRule -import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.provider.DocumentsContract +import android.service.chooser.FakeFeatureFlagsImpl +import android.service.chooser.Flags import com.google.common.truth.Truth.assertThat import kotlin.coroutines.EmptyCoroutineContext import kotlinx.coroutines.CoroutineScope @@ -32,7 +32,6 @@ import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest -import org.junit.Rule import org.junit.Test import org.mockito.kotlin.any import org.mockito.kotlin.mock @@ -46,7 +45,8 @@ class PreviewDataProviderTest { private val contentResolver = mock<ContentInterface>() private val mimeTypeClassifier = DefaultMimeTypeClassifier private val testScope = TestScope(EmptyCoroutineContext + UnconfinedTestDispatcher()) - @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + private val featureFlags = + FakeFeatureFlagsImpl().apply { setFlag(Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING, false) } private fun createDataProvider( targetIntent: Intent, @@ -54,14 +54,13 @@ class PreviewDataProviderTest { additionalContentUri: Uri? = null, resolver: ContentInterface = contentResolver, typeClassifier: MimeTypeClassifier = mimeTypeClassifier, - isPayloadTogglingEnabled: Boolean = false ) = PreviewDataProvider( scope, targetIntent, additionalContentUri, resolver, - isPayloadTogglingEnabled, + featureFlags, typeClassifier, ) @@ -377,11 +376,11 @@ class PreviewDataProviderTest { val uri = Uri.parse("content://org.pkg.app/image.png") val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) } whenever(contentResolver.getType(uri)).thenReturn("image/png") + featureFlags.setFlag(Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING, true) val testSubject = createDataProvider( targetIntent, additionalContentUri = Uri.parse("content://org.pkg.app.extracontent"), - isPayloadTogglingEnabled = true, ) assertThat(testSubject.previewType) @@ -415,11 +414,11 @@ class PreviewDataProviderTest { val uri = Uri.parse("content://org.pkg.app/image.png") val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) } whenever(contentResolver.getType(uri)).thenReturn("image/png") + featureFlags.setFlag(Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING, true) val testSubject = createDataProvider( targetIntent, additionalContentUri = Uri.parse("content://org.pkg.app/extracontent"), - isPayloadTogglingEnabled = true, ) assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE) |