summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/activity/src/com/android/intentresolver/ChooserActivityOverrideData.java3
-rw-r--r--tests/activity/src/com/android/intentresolver/ChooserActivityTest.java8
-rw-r--r--tests/activity/src/com/android/intentresolver/ChooserWrapperActivity.java14
-rw-r--r--tests/shared/src/com/android/intentresolver/FakeImageLoader.kt12
-rw-r--r--tests/shared/src/com/android/intentresolver/TestContentPreviewViewModel.kt64
-rw-r--r--tests/shared/src/com/android/intentresolver/contentpreview/FakeThumbnailLoader.kt13
-rw-r--r--tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/PayloadToggleInteractorKosmos.kt1
-rw-r--r--tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackKosmos.kt2
-rw-r--r--tests/shared/src/com/android/intentresolver/logging/FakeEventLog.kt9
-rw-r--r--tests/unit/Android.bp1
-rw-r--r--tests/unit/src/com/android/intentresolver/ChooserActionFactoryTest.kt6
-rw-r--r--tests/unit/src/com/android/intentresolver/ChooserListAdapterDataTest.kt8
-rw-r--r--tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt15
-rw-r--r--tests/unit/src/com/android/intentresolver/ResolverListAdapterTest.kt41
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/CachingImagePreviewImageLoaderTest.kt36
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt26
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt48
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt357
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/PreviewImageLoaderTest.kt496
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/cursor/PayloadToggleCursorResolverTest.kt6
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractorTest.kt89
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractorTest.kt6
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractorTest.kt35
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractorTest.kt24
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackImplTest.kt64
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModelTest.kt77
-rw-r--r--tests/unit/src/com/android/intentresolver/ext/CreationExtrasExtTest.kt15
-rw-r--r--tests/unit/src/com/android/intentresolver/logging/EventLogImplTest.java39
-rw-r--r--tests/unit/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt169
-rw-r--r--tests/unit/src/com/android/intentresolver/ui/ShareResultSenderImplTest.kt78
-rw-r--r--tests/unit/src/com/android/intentresolver/ui/model/ActivityModelTest.kt7
-rw-r--r--tests/unit/src/com/android/intentresolver/ui/viewmodel/ChooserRequestTest.kt45
-rw-r--r--tests/unit/src/com/android/intentresolver/ui/viewmodel/ResolverRequestTest.kt9
-rw-r--r--tests/unit/src/com/android/intentresolver/widget/BatchPreviewLoaderTest.kt28
34 files changed, 1432 insertions, 419 deletions
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..e103e57b 100644
--- a/tests/activity/src/com/android/intentresolver/ChooserActivityTest.java
+++ b/tests/activity/src/com/android/intentresolver/ChooserActivityTest.java
@@ -124,6 +124,7 @@ import com.android.intentresolver.contentpreview.ImageLoaderModule;
import com.android.intentresolver.contentpreview.PreviewCacheSize;
import com.android.intentresolver.contentpreview.PreviewMaxConcurrency;
import com.android.intentresolver.contentpreview.ThumbnailLoader;
+import com.android.intentresolver.contentpreview.ThumbnailSize;
import com.android.intentresolver.data.repository.FakeUserRepository;
import com.android.intentresolver.data.repository.UserRepository;
import com.android.intentresolver.data.repository.UserRepositoryModule;
@@ -285,6 +286,10 @@ public class ChooserActivityTest {
int mPreviewMaxConcurrency = 4;
@BindValue
+ @ThumbnailSize
+ int mPreviewThumbnailSize = 500;
+
+ @BindValue
ThumbnailLoader mThumbnailLoader = new FakeThumbnailLoader();
@Before
@@ -305,9 +310,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..6ff7af3f 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;
@@ -67,7 +65,7 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW
initialIntents,
rList,
filterLastUsed,
- createListController(userHandle),
+ resolverListController,
userHandle,
targetIntent,
referrerFillInIntent,
@@ -77,8 +75,7 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW
maxTargetsPerRow,
userHandle,
mTargetDataLoader,
- null,
- mFeatureFlags);
+ null);
}
@Override
@@ -152,13 +149,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/FakeImageLoader.kt b/tests/shared/src/com/android/intentresolver/FakeImageLoader.kt
index c57ea78b..76eb5e0d 100644
--- a/tests/shared/src/com/android/intentresolver/FakeImageLoader.kt
+++ b/tests/shared/src/com/android/intentresolver/FakeImageLoader.kt
@@ -18,6 +18,7 @@ package com.android.intentresolver
import android.graphics.Bitmap
import android.net.Uri
+import android.util.Size
import com.android.intentresolver.contentpreview.ImageLoader
import java.util.function.Consumer
import kotlinx.coroutines.CoroutineScope
@@ -25,13 +26,18 @@ import kotlinx.coroutines.CoroutineScope
class FakeImageLoader(initialBitmaps: Map<Uri, Bitmap> = emptyMap()) : ImageLoader {
private val bitmaps = HashMap<Uri, Bitmap>().apply { putAll(initialBitmaps) }
- override fun loadImage(callerScope: CoroutineScope, uri: Uri, callback: Consumer<Bitmap?>) {
+ override fun loadImage(
+ callerScope: CoroutineScope,
+ uri: Uri,
+ size: Size,
+ callback: Consumer<Bitmap?>,
+ ) {
callback.accept(bitmaps[uri])
}
- override suspend fun invoke(uri: Uri, caching: Boolean): Bitmap? = bitmaps[uri]
+ override suspend fun invoke(uri: Uri, size: Size, caching: Boolean): Bitmap? = bitmaps[uri]
- override fun prePopulate(uris: List<Uri>) = Unit
+ override fun prePopulate(uriSizePairs: List<Pair<Uri, Size>>) = Unit
fun setBitmap(uri: Uri, bitmap: Bitmap) {
bitmaps[uri] = bitmap
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/shared/src/com/android/intentresolver/contentpreview/FakeThumbnailLoader.kt b/tests/shared/src/com/android/intentresolver/contentpreview/FakeThumbnailLoader.kt
index d3fdf17d..33969eb7 100644
--- a/tests/shared/src/com/android/intentresolver/contentpreview/FakeThumbnailLoader.kt
+++ b/tests/shared/src/com/android/intentresolver/contentpreview/FakeThumbnailLoader.kt
@@ -18,18 +18,23 @@ package com.android.intentresolver.contentpreview
import android.graphics.Bitmap
import android.net.Uri
+import android.util.Size
/** Fake implementation of [ThumbnailLoader] for use in testing. */
-class FakeThumbnailLoader : ThumbnailLoader {
+class FakeThumbnailLoader(private val defaultSize: Size = Size(100, 100)) : ThumbnailLoader {
- val fakeInvoke = mutableMapOf<Uri, suspend () -> Bitmap?>()
+ val fakeInvoke = mutableMapOf<Uri, suspend (Size) -> Bitmap?>()
val invokeCalls = mutableListOf<Uri>()
var unfinishedInvokeCount = 0
- override suspend fun invoke(uri: Uri): Bitmap? {
+ override suspend fun loadThumbnail(uri: Uri): Bitmap? = getBitmap(uri, defaultSize)
+
+ override suspend fun loadThumbnail(uri: Uri, size: Size): Bitmap? = getBitmap(uri, size)
+
+ private suspend fun getBitmap(uri: Uri, size: Size): Bitmap? {
invokeCalls.add(uri)
unfinishedInvokeCount++
- val result = fakeInvoke[uri]?.invoke()
+ val result = fakeInvoke[uri]?.invoke(size)
unfinishedInvokeCount--
return result
}
diff --git a/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/PayloadToggleInteractorKosmos.kt b/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/PayloadToggleInteractorKosmos.kt
index cb88cd9e..7cca414f 100644
--- a/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/PayloadToggleInteractorKosmos.kt
+++ b/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/PayloadToggleInteractorKosmos.kt
@@ -91,6 +91,7 @@ val Kosmos.selectablePreviewsInteractor
SelectablePreviewsInteractor(
previewsRepo = cursorPreviewsRepository,
selectionInteractor = selectionInteractor,
+ eventLog = eventLog,
)
val Kosmos.selectionInteractor
diff --git a/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackKosmos.kt b/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackKosmos.kt
index 548b1f37..b26b562e 100644
--- a/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackKosmos.kt
+++ b/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackKosmos.kt
@@ -19,7 +19,6 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.update
import com.android.intentresolver.contentInterface
import com.android.intentresolver.inject.additionalContentUri
import com.android.intentresolver.inject.chooserIntent
-import com.android.intentresolver.inject.chooserServiceFlags
import com.android.systemui.kosmos.Kosmos
val Kosmos.selectionChangeCallbackImpl by
@@ -28,7 +27,6 @@ val Kosmos.selectionChangeCallbackImpl by
additionalContentUri,
chooserIntent,
contentInterface,
- chooserServiceFlags,
)
}
var Kosmos.selectionChangeCallback: SelectionChangeCallback by
diff --git a/tests/shared/src/com/android/intentresolver/logging/FakeEventLog.kt b/tests/shared/src/com/android/intentresolver/logging/FakeEventLog.kt
index 9ed47db6..c2d13f1e 100644
--- a/tests/shared/src/com/android/intentresolver/logging/FakeEventLog.kt
+++ b/tests/shared/src/com/android/intentresolver/logging/FakeEventLog.kt
@@ -164,14 +164,22 @@ class FakeEventLog @Inject constructor(private val instanceId: InstanceId) : Eve
log { "logSharesheetEmptyDirectShareRow()" }
}
+ override fun logPayloadSelectionChanged() {
+ log { "logPayloadSelectionChanged" }
+ }
+
data class ActionSelected(val targetType: Int)
+
data class CustomActionSelected(val positionPicked: Int)
+
data class ActionShareWithPreview(val previewType: Int)
+
data class ChooserActivityShown(
val isWorkProfile: Boolean,
val targetMimeType: String?,
val systemCost: Long
)
+
data class ShareStarted(
val packageName: String?,
val mimeType: String?,
@@ -183,6 +191,7 @@ class FakeEventLog @Inject constructor(private val instanceId: InstanceId) : Eve
val customActionCount: Int,
val modifyShareActionProvided: Boolean
)
+
data class ShareTargetSelected(
val targetType: Int,
val packageName: String?,
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 857d44aa..a3b30a3a 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -33,6 +33,7 @@ android_test {
"android.test.mock.stubs.system",
"framework",
"framework-res",
+ "flag-junit",
],
resource_dirs: ["res"],
diff --git a/tests/unit/src/com/android/intentresolver/ChooserActionFactoryTest.kt b/tests/unit/src/com/android/intentresolver/ChooserActionFactoryTest.kt
index c8e17de4..8dfbdbdd 100644
--- a/tests/unit/src/com/android/intentresolver/ChooserActionFactoryTest.kt
+++ b/tests/unit/src/com/android/intentresolver/ChooserActionFactoryTest.kt
@@ -69,8 +69,6 @@ class ChooserActionFactoryTest {
latestReturn = resultCode
}
}
- private val featureFlags =
- FakeFeatureFlagsImpl().apply { setFlag(Flags.FLAG_FIX_PARTIAL_IMAGE_EDIT_TRANSITION, true) }
@Before
fun setup() {
@@ -121,7 +119,6 @@ class ChooserActionFactoryTest {
/* shareResultSender = */ null,
/* finishCallback = */ {},
/* clipboardManager = */ mock(),
- /* featureFlags = */ featureFlags,
)
assertThat(testSubject.copyButtonRunnable).isNull()
}
@@ -143,7 +140,6 @@ class ChooserActionFactoryTest {
/* shareResultSender = */ null,
/* finishCallback = */ {},
/* clipboardManager = */ mock(),
- /* featureFlags = */ featureFlags,
)
assertThat(testSubject.copyButtonRunnable).isNull()
}
@@ -166,7 +162,6 @@ class ChooserActionFactoryTest {
/* shareResultSender = */ resultSender,
/* finishCallback = */ {},
/* clipboardManager = */ mock(),
- /* featureFlags = */ featureFlags,
)
assertThat(testSubject.copyButtonRunnable).isNotNull()
@@ -199,7 +194,6 @@ class ChooserActionFactoryTest {
/* shareResultSender = */ null,
/* finishCallback = */ resultConsumer,
/* clipboardManager = */ mock(),
- /* featureFlags = */ featureFlags,
)
}
}
diff --git a/tests/unit/src/com/android/intentresolver/ChooserListAdapterDataTest.kt b/tests/unit/src/com/android/intentresolver/ChooserListAdapterDataTest.kt
index df0c5e5e..bbef6c0c 100644
--- a/tests/unit/src/com/android/intentresolver/ChooserListAdapterDataTest.kt
+++ b/tests/unit/src/com/android/intentresolver/ChooserListAdapterDataTest.kt
@@ -66,8 +66,6 @@ class ChooserListAdapterDataTest {
private val immediateExecutor = TestExecutor(immediate = true)
private val referrerFillInIntent =
Intent().putExtra(Intent.EXTRA_REFERRER, "org.referrer.package")
- private val featureFlags =
- FakeFeatureFlagsImpl().apply { setFlag(Flags.FLAG_BESPOKE_LABEL_VIEW, false) }
@Test
fun test_twoTargetsWithNonOverlappingInitialIntent_threeTargetsInResolverAdapter() {
@@ -86,7 +84,7 @@ class ChooserListAdapterDataTest {
userHandle
)
)
- .thenReturn(resolvedTargets)
+ .thenReturn(ArrayList(resolvedTargets))
val initialActivityInfo = createActivityInfo(3)
val initialIntents =
arrayOf(
@@ -119,7 +117,6 @@ class ChooserListAdapterDataTest {
null,
backgroundExecutor,
immediateExecutor,
- featureFlags,
)
val doPostProcessing = true
@@ -152,7 +149,7 @@ class ChooserListAdapterDataTest {
userHandle
)
)
- .thenReturn(resolvedTargets)
+ .thenReturn(ArrayList(resolvedTargets))
val activityInfo = resolvedTargets[1].getResolveInfoAt(0).activityInfo
val initialIntents =
arrayOf(Intent(Intent.ACTION_SEND).apply { component = activityInfo.componentName })
@@ -183,7 +180,6 @@ class ChooserListAdapterDataTest {
null,
backgroundExecutor,
immediateExecutor,
- featureFlags,
)
val doPostProcessing = true
diff --git a/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt b/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt
index bad3b18c..cdc84ba8 100644
--- a/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt
+++ b/tests/unit/src/com/android/intentresolver/ChooserListAdapterTest.kt
@@ -61,7 +61,6 @@ class ChooserListAdapterTest {
private val mEventLog = mock<EventLogImpl>()
private val mTargetDataLoader = mock<TargetDataLoader>()
private val mPackageChangeCallback = mock<ChooserListAdapter.PackageChangeCallback>()
- private val featureFlags = FeatureFlagsImpl()
private val testSubject by lazy {
ChooserListAdapter(
@@ -81,7 +80,6 @@ class ChooserListAdapterTest {
null,
mTargetDataLoader,
mPackageChangeCallback,
- featureFlags,
)
}
@@ -222,15 +220,10 @@ class ChooserListAdapterTest {
private fun createView(): View {
val view = FrameLayout(context)
- if (featureFlags.bespokeLabelView()) {
- BadgeTextView(context)
- } else {
- TextView(context)
- }
- .apply {
- id = R.id.text1
- view.addView(this)
- }
+ BadgeTextView(context).apply {
+ id = R.id.text1
+ view.addView(this)
+ }
TextView(context).apply {
id = R.id.text2
view.addView(this)
diff --git a/tests/unit/src/com/android/intentresolver/ResolverListAdapterTest.kt b/tests/unit/src/com/android/intentresolver/ResolverListAdapterTest.kt
index d8cb7adc..23ea33b2 100644
--- a/tests/unit/src/com/android/intentresolver/ResolverListAdapterTest.kt
+++ b/tests/unit/src/com/android/intentresolver/ResolverListAdapterTest.kt
@@ -79,7 +79,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
}
val testSubject =
ResolverListAdapter(
@@ -128,7 +128,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
on { lastChosen } doReturn resolvedTargets[0].getResolveInfoAt(0)
}
val testSubject =
@@ -177,7 +177,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
on { lastChosen } doReturn createResolveInfo(PKG_NAME_TWO, CLASS_NAME, userHandle)
}
val testSubject =
@@ -228,7 +228,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
on { lastChosen } doReturn resolvedTargets[0].getResolveInfoAt(0)
}
val testSubject =
@@ -302,7 +302,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
if (hasLastChosen) {
on { lastChosen } doReturn resolvedTargets[0].getResolveInfoAt(0)
}
@@ -379,7 +379,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
on { lastChosen } doReturn createResolveInfo(PKG_NAME, CLASS_NAME + "2", userHandle)
}
val testSubject =
@@ -434,7 +434,6 @@ class ResolverListAdapterTest {
ComponentName(PKG_NAME_TWO, CLASS_NAME),
)
resolvedTargets[1].getResolveInfoAt(0).targetUserId = 10
- // whenever(resolvedTargets[1].getResolveInfoAt(0).loadLabel(any())).thenReturn("Label")
val resolverListController =
mock<ResolverListController> {
on { filterIneligibleActivities(any(), any()) } doReturn null
@@ -447,7 +446,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
on { lastChosen } doReturn resolvedTargets[0].getResolveInfoAt(0)
}
val testSubject =
@@ -477,7 +476,9 @@ class ResolverListAdapterTest {
assertThat(testSubject.hasFilteredItem()).isFalse()
assertThat(testSubject.filteredItem).isNull()
assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
+ // The following must be an old bug i.e. unfilteredResolveList should be equal to
+ // resolvedTargets. Also see comments in the code.
+ assertThat(testSubject.unfilteredResolveList).containsExactly(resolvedTargets[0])
assertThat(testSubject.isTabLoaded).isTrue()
assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(0)
}
@@ -502,7 +503,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
on { sort(any()) } doAnswer
{
val components = it.arguments[0] as MutableList<ResolvedComponentInfo>
@@ -532,11 +533,10 @@ class ResolverListAdapterTest {
backgroundExecutor.runUntilIdle()
- // we don't reset placeholder count (legacy logic, likely an oversight?)
assertThat(testSubject.count).isEqualTo(resolvedTargets.size)
- assertThat(resolvedTargets[0].getResolveInfoAt(0).activityInfo.packageName)
+ assertThat(testSubject.getDisplayResolveInfo(0).resolveInfo.activityInfo.packageName)
.isEqualTo(PKG_NAME_TWO)
- assertThat(resolvedTargets[1].getResolveInfoAt(0).activityInfo.packageName)
+ assertThat(testSubject.getDisplayResolveInfo(1).resolveInfo.activityInfo.packageName)
.isEqualTo(PKG_NAME)
}
@@ -560,7 +560,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
on { filterIneligibleActivities(any(), any()) } doAnswer
{
val components = it.arguments[0] as MutableList<ResolvedComponentInfo>
@@ -646,7 +646,6 @@ class ResolverListAdapterTest {
backgroundExecutor.runUntilIdle()
- // we don't reset placeholder count (legacy logic, likely an oversight?)
assertThat(testSubject.count).isEqualTo(2)
assertThat(testSubject.unfilteredResolveList).hasSize(2)
}
@@ -670,7 +669,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
on { filterLowPriority(any(), any()) } doAnswer
{
val components = it.arguments[0] as MutableList<ResolvedComponentInfo>
@@ -730,7 +729,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
}
whenever(packageManager.getActivityInfo(eq(initialComponent), eq(0)))
.thenReturn(createActivityInfo(initialComponent))
@@ -801,7 +800,7 @@ class ResolverListAdapterTest {
payloadIntents,
userHandle
)
- } doReturn resolvedTargets
+ } doReturn ArrayList(resolvedTargets)
}
val initialComponent = ComponentName(PKG_NAME_TWO, CLASS_NAME)
val initialIntents =
@@ -896,7 +895,7 @@ class ResolverListAdapterTest {
on { filterIneligibleActivities(any(), any()) } doReturn null
on { filterLowPriority(any(), any()) } doReturn null
on { getResolversForIntentAsUser(any(), any(), any(), any(), any()) } doReturn
- resolvedTargets
+ ArrayList(resolvedTargets)
}
val communicator =
mock<ResolverListCommunicator> {
@@ -944,7 +943,7 @@ class ResolverListAdapterTest {
on { filterIneligibleActivities(any(), any()) } doReturn null
on { filterLowPriority(any(), any()) } doReturn null
on { getResolversForIntentAsUser(any(), any(), any(), any(), any()) } doReturn
- resolvedTargets
+ ArrayList(resolvedTargets)
}
val communicator =
mock<ResolverListCommunicator> {
@@ -999,7 +998,7 @@ class ResolverListAdapterTest {
on { filterIneligibleActivities(any(), any()) } doReturn null
on { filterLowPriority(any(), any()) } doReturn null
on { getResolversForIntentAsUser(any(), any(), any(), any(), any()) } doReturn
- resolvedTargets
+ ArrayList(resolvedTargets)
}
val communicator =
mock<ResolverListCommunicator> {
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/CachingImagePreviewImageLoaderTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/CachingImagePreviewImageLoaderTest.kt
index 331f9f64..d5a569aa 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/CachingImagePreviewImageLoaderTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/CachingImagePreviewImageLoaderTest.kt
@@ -18,6 +18,7 @@ package com.android.intentresolver.contentpreview
import android.graphics.Bitmap
import android.net.Uri
+import android.util.Size
import com.google.common.truth.Truth.assertThat
import kotlin.math.ceil
import kotlin.math.roundToInt
@@ -43,6 +44,7 @@ class CachingImagePreviewImageLoaderTest {
testJobTime * ceil((testCacheSize).toFloat() / testMaxConcurrency.toFloat()).roundToInt()
private val testUris =
List(5) { Uri.fromParts("TestScheme$it", "TestSsp$it", "TestFragment$it") }
+ private val previewSize = Size(500, 500)
private val testTimeToLoadAllUris =
testJobTime * ceil((testUris.size).toFloat() / testMaxConcurrency.toFloat()).roundToInt()
private val testBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8)
@@ -72,7 +74,7 @@ class CachingImagePreviewImageLoaderTest {
var result: Bitmap? = null
// Act
- imageLoader.loadImage(testScope, testUris[0]) { result = it }
+ imageLoader.loadImage(testScope, testUris[0], previewSize) { result = it }
advanceTimeBy(testJobTime)
runCurrent()
@@ -85,14 +87,14 @@ class CachingImagePreviewImageLoaderTest {
fun loadImage_cached_usesCachedValue() =
testScope.runTest {
// Arrange
- imageLoader.loadImage(testScope, testUris[0]) {}
+ imageLoader.loadImage(testScope, testUris[0], previewSize) {}
advanceTimeBy(testJobTime)
runCurrent()
fakeThumbnailLoader.invokeCalls.clear()
var result: Bitmap? = null
// Act
- imageLoader.loadImage(testScope, testUris[0]) { result = it }
+ imageLoader.loadImage(testScope, testUris[0], previewSize) { result = it }
advanceTimeBy(testJobTime)
runCurrent()
@@ -112,7 +114,7 @@ class CachingImagePreviewImageLoaderTest {
var result: Bitmap? = testBitmap
// Act
- imageLoader.loadImage(testScope, testUris[0]) { result = it }
+ imageLoader.loadImage(testScope, testUris[0], previewSize) { result = it }
advanceTimeBy(testJobTime)
runCurrent()
@@ -130,7 +132,7 @@ class CachingImagePreviewImageLoaderTest {
// Act
testUris.take(testMaxConcurrency + 1).forEach { uri ->
- imageLoader.loadImage(testScope, uri) { results.add(it) }
+ imageLoader.loadImage(testScope, uri, previewSize) { results.add(it) }
}
// Assert
@@ -153,10 +155,10 @@ class CachingImagePreviewImageLoaderTest {
assertThat(testUris.size).isGreaterThan(testCacheSize)
// Act
- imageLoader.loadImage(testScope, testUris[0]) { results[0] = it }
+ imageLoader.loadImage(testScope, testUris[0], previewSize) { results[0] = it }
runCurrent()
testUris.indices.drop(1).take(testCacheSize).forEach { i ->
- imageLoader.loadImage(testScope, testUris[i]) { results[i] = it }
+ imageLoader.loadImage(testScope, testUris[i], previewSize) { results[i] = it }
}
advanceTimeBy(testTimeToFillCache)
runCurrent()
@@ -179,7 +181,7 @@ class CachingImagePreviewImageLoaderTest {
assertThat(fullCacheUris).hasSize(testCacheSize)
// Act
- imageLoader.prePopulate(fullCacheUris)
+ imageLoader.prePopulate(fullCacheUris.map { it to previewSize })
advanceTimeBy(testTimeToFillCache)
runCurrent()
@@ -188,7 +190,7 @@ class CachingImagePreviewImageLoaderTest {
// Act
fakeThumbnailLoader.invokeCalls.clear()
- imageLoader.prePopulate(fullCacheUris)
+ imageLoader.prePopulate(fullCacheUris.map { it to previewSize })
advanceTimeBy(testTimeToFillCache)
runCurrent()
@@ -203,7 +205,7 @@ class CachingImagePreviewImageLoaderTest {
assertThat(testUris.size).isGreaterThan(testCacheSize)
// Act
- imageLoader.prePopulate(testUris)
+ imageLoader.prePopulate(testUris.map { it to previewSize })
advanceTimeBy(testTimeToLoadAllUris)
runCurrent()
@@ -213,7 +215,7 @@ class CachingImagePreviewImageLoaderTest {
// Act
fakeThumbnailLoader.invokeCalls.clear()
- imageLoader.prePopulate(testUris)
+ imageLoader.prePopulate(testUris.map { it to previewSize })
advanceTimeBy(testTimeToLoadAllUris)
runCurrent()
@@ -229,7 +231,7 @@ class CachingImagePreviewImageLoaderTest {
assertThat(unfilledCacheUris.size).isLessThan(testCacheSize)
// Act
- imageLoader.prePopulate(unfilledCacheUris)
+ imageLoader.prePopulate(unfilledCacheUris.map { it to previewSize })
advanceTimeBy(testJobTime)
runCurrent()
@@ -238,7 +240,7 @@ class CachingImagePreviewImageLoaderTest {
// Act
fakeThumbnailLoader.invokeCalls.clear()
- imageLoader.prePopulate(unfilledCacheUris)
+ imageLoader.prePopulate(unfilledCacheUris.map { it to previewSize })
advanceTimeBy(testJobTime)
runCurrent()
@@ -252,8 +254,8 @@ class CachingImagePreviewImageLoaderTest {
// Arrange
// Act
- imageLoader.invoke(testUris[0], caching = false)
- imageLoader.invoke(testUris[0], caching = false)
+ imageLoader.invoke(testUris[0], previewSize, caching = false)
+ imageLoader.invoke(testUris[0], previewSize, caching = false)
advanceTimeBy(testJobTime)
runCurrent()
@@ -267,8 +269,8 @@ class CachingImagePreviewImageLoaderTest {
// Arrange
// Act
- imageLoader.invoke(testUris[0], caching = true)
- imageLoader.invoke(testUris[0], caching = true)
+ imageLoader.invoke(testUris[0], previewSize, caching = true)
+ imageLoader.invoke(testUris[0], previewSize, caching = true)
advanceTimeBy(testJobTime)
runCurrent()
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
index 27d98ece..905c8517 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
@@ -23,6 +23,7 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider
import com.android.intentresolver.ContentTypeHint
import com.android.intentresolver.FakeImageLoader
import com.android.intentresolver.contentpreview.ChooserContentPreviewUi.ActionFactory
+import com.android.intentresolver.data.model.ChooserRequest
import com.android.intentresolver.widget.ActionRow
import com.android.intentresolver.widget.ImagePreviewView
import com.google.common.truth.Truth.assertThat
@@ -61,13 +62,18 @@ class ChooserContentPreviewUiTest {
@get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private fun createContentPreviewUi(
- targetIntent: Intent,
+ action: String,
+ sharedText: CharSequence? = null,
isPayloadTogglingEnabled: Boolean = false
) =
ChooserContentPreviewUi(
testScope,
previewData,
- targetIntent,
+ ChooserRequest(
+ targetIntent = Intent(action),
+ sharedText = sharedText,
+ launchedFromPackage = "org.pkg",
+ ),
imageLoader,
actionFactory,
{ null },
@@ -81,7 +87,7 @@ class ChooserContentPreviewUiTest {
@Test
fun test_textPreviewType_useTextPreviewUi() {
whenever(previewData.previewType).thenReturn(ContentPreviewType.CONTENT_PREVIEW_TEXT)
- val testSubject = createContentPreviewUi(targetIntent = Intent(Intent.ACTION_VIEW))
+ val testSubject = createContentPreviewUi(action = Intent.ACTION_VIEW)
assertThat(testSubject.preferredContentPreview)
.isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT)
@@ -92,7 +98,7 @@ class ChooserContentPreviewUiTest {
@Test
fun test_filePreviewType_useFilePreviewUi() {
whenever(previewData.previewType).thenReturn(ContentPreviewType.CONTENT_PREVIEW_FILE)
- val testSubject = createContentPreviewUi(targetIntent = Intent(Intent.ACTION_SEND))
+ val testSubject = createContentPreviewUi(action = Intent.ACTION_SEND)
assertThat(testSubject.preferredContentPreview)
.isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
assertThat(testSubject.mContentPreviewUi).isInstanceOf(FileContentPreviewUi::class.java)
@@ -109,8 +115,8 @@ class ChooserContentPreviewUiTest {
whenever(previewData.imagePreviewFileInfoFlow).thenReturn(MutableSharedFlow())
val testSubject =
createContentPreviewUi(
- targetIntent =
- Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_TEXT, "Shared text") }
+ action = Intent.ACTION_SEND,
+ sharedText = "Shared text",
)
assertThat(testSubject.mContentPreviewUi)
.isInstanceOf(FilesPlusTextContentPreviewUi::class.java)
@@ -126,7 +132,7 @@ class ChooserContentPreviewUiTest {
whenever(previewData.firstFileInfo)
.thenReturn(FileInfo.Builder(uri).withPreviewUri(uri).withMimeType("image/png").build())
whenever(previewData.imagePreviewFileInfoFlow).thenReturn(MutableSharedFlow())
- val testSubject = createContentPreviewUi(targetIntent = Intent(Intent.ACTION_SEND))
+ val testSubject = createContentPreviewUi(action = Intent.ACTION_SEND)
assertThat(testSubject.preferredContentPreview)
.isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
assertThat(testSubject.mContentPreviewUi).isInstanceOf(UnifiedContentPreviewUi::class.java)
@@ -146,10 +152,12 @@ class ChooserContentPreviewUiTest {
whenever(previewData.imagePreviewFileInfoFlow).thenReturn(MutableSharedFlow())
val testSubject =
createContentPreviewUi(
- targetIntent = Intent(Intent.ACTION_SEND),
- isPayloadTogglingEnabled = true
+ action = Intent.ACTION_SEND,
+ isPayloadTogglingEnabled = true,
)
assertThat(testSubject.mContentPreviewUi)
.isInstanceOf(ShareouselContentPreviewUi::class.java)
+ assertThat(testSubject.preferredContentPreview)
+ .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION)
}
}
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt
index 3a45e2f6..d78e6665 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt
@@ -77,24 +77,25 @@ class ImagePreviewImageLoaderTest {
contentResolver,
cacheSize = 1,
)
+ private val previewSize = Size(500, 500)
@Test
fun prePopulate_cachesImagesUpToTheCacheSize() =
scope.runTest {
- testSubject.prePopulate(listOf(uriOne, uriTwo))
+ testSubject.prePopulate(listOf(uriOne to previewSize, uriTwo to previewSize))
verify(contentResolver, times(1)).loadThumbnail(uriOne, imageSize, null)
verify(contentResolver, never()).loadThumbnail(uriTwo, imageSize, null)
- testSubject(uriOne)
+ testSubject(uriOne, previewSize)
verify(contentResolver, times(1)).loadThumbnail(uriOne, imageSize, null)
}
@Test
fun invoke_returnCachedImageWhenCalledTwice() =
scope.runTest {
- testSubject(uriOne)
- testSubject(uriOne)
+ testSubject(uriOne, previewSize)
+ testSubject(uriOne, previewSize)
verify(contentResolver, times(1)).loadThumbnail(any(), any(), anyOrNull())
}
@@ -102,8 +103,8 @@ class ImagePreviewImageLoaderTest {
@Test
fun invoke_whenInstructed_doesNotCache() =
scope.runTest {
- testSubject(uriOne, false)
- testSubject(uriOne, false)
+ testSubject(uriOne, previewSize, false)
+ testSubject(uriOne, previewSize, false)
verify(contentResolver, times(2)).loadThumbnail(any(), any(), anyOrNull())
}
@@ -120,8 +121,8 @@ class ImagePreviewImageLoaderTest {
cacheSize = 1,
)
coroutineScope {
- launch(start = UNDISPATCHED) { testSubject(uriOne, false) }
- launch(start = UNDISPATCHED) { testSubject(uriOne, false) }
+ launch(start = UNDISPATCHED) { testSubject(uriOne, previewSize, false) }
+ launch(start = UNDISPATCHED) { testSubject(uriOne, previewSize, false) }
scheduler.advanceUntilIdle()
}
@@ -131,10 +132,10 @@ class ImagePreviewImageLoaderTest {
@Test
fun invoke_oldRecordsEvictedFromTheCache() =
scope.runTest {
- testSubject(uriOne)
- testSubject(uriTwo)
- testSubject(uriTwo)
- testSubject(uriOne)
+ testSubject(uriOne, previewSize)
+ testSubject(uriTwo, previewSize)
+ testSubject(uriTwo, previewSize)
+ testSubject(uriOne, previewSize)
verify(contentResolver, times(2)).loadThumbnail(uriOne, imageSize, null)
verify(contentResolver, times(1)).loadThumbnail(uriTwo, imageSize, null)
@@ -144,8 +145,8 @@ class ImagePreviewImageLoaderTest {
fun invoke_doNotCacheNulls() =
scope.runTest {
whenever(contentResolver.loadThumbnail(any(), any(), anyOrNull())).thenReturn(null)
- testSubject(uriOne)
- testSubject(uriOne)
+ testSubject(uriOne, previewSize)
+ testSubject(uriOne, previewSize)
verify(contentResolver, times(2)).loadThumbnail(uriOne, imageSize, null)
}
@@ -162,7 +163,7 @@ class ImagePreviewImageLoaderTest {
cacheSize = 1,
)
imageLoaderScope.cancel()
- testSubject(uriOne)
+ testSubject(uriOne, previewSize)
}
@Test(expected = CancellationException::class)
@@ -178,7 +179,8 @@ class ImagePreviewImageLoaderTest {
cacheSize = 1,
)
coroutineScope {
- val deferred = async(start = UNDISPATCHED) { testSubject(uriOne, false) }
+ val deferred =
+ async(start = UNDISPATCHED) { testSubject(uriOne, previewSize, false) }
imageLoaderScope.cancel()
scheduler.advanceUntilIdle()
deferred.await()
@@ -198,11 +200,11 @@ class ImagePreviewImageLoaderTest {
cacheSize = 1,
)
coroutineScope {
- launch(start = UNDISPATCHED) { testSubject(uriOne, false) }
- launch(start = UNDISPATCHED) { testSubject(uriOne, true) }
+ launch(start = UNDISPATCHED) { testSubject(uriOne, previewSize, false) }
+ launch(start = UNDISPATCHED) { testSubject(uriOne, previewSize, true) }
scheduler.advanceUntilIdle()
}
- testSubject(uriOne, true)
+ testSubject(uriOne, previewSize, true)
verify(contentResolver, times(1)).loadThumbnail(uriOne, imageSize, null)
}
@@ -243,7 +245,7 @@ class ImagePreviewImageLoaderTest {
cacheSize = 1,
testSemaphore,
)
- testSubject(uriOne, false)
+ testSubject(uriOne, previewSize, false)
verify(contentResolver, times(1)).loadThumbnail(uriOne, imageSize, null)
assertThat(acquireCount.get()).isEqualTo(1)
@@ -281,7 +283,7 @@ class ImagePreviewImageLoaderTest {
cacheSize = 1,
testSemaphore,
)
- launch(start = UNDISPATCHED) { testSubject(uriOne, false) }
+ launch(start = UNDISPATCHED) { testSubject(uriOne, previewSize, false) }
verify(contentResolver, never()).loadThumbnail(any(), any(), anyOrNull())
@@ -324,7 +326,9 @@ class ImagePreviewImageLoaderTest {
)
coroutineScope {
repeat(requestCount) {
- launch { testSubject(Uri.parse("content://org.pkg.app/image-$it.png")) }
+ launch {
+ testSubject(Uri.parse("content://org.pkg.app/image-$it.png"), previewSize)
+ }
}
yield()
// wait for all requests to be dispatched
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt
index a2fb9693..3dae760c 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt
@@ -21,9 +21,15 @@ 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.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.SetFlagsRule
import android.provider.DocumentsContract
+import android.provider.Downloads
+import android.provider.OpenableColumns
+import android.service.chooser.Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING
+import com.android.intentresolver.Flags.FLAG_INDIVIDUAL_METADATA_TITLE_READ
import com.google.common.truth.Truth.assertThat
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CoroutineScope
@@ -34,19 +40,24 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+@RunWith(Parameterized::class)
@OptIn(ExperimentalCoroutinesApi::class)
-class PreviewDataProviderTest {
+class PreviewDataProviderTest(flags: FlagsParameterization) {
private val contentResolver = mock<ContentInterface>()
private val mimeTypeClassifier = DefaultMimeTypeClassifier
private val testScope = TestScope(EmptyCoroutineContext + UnconfinedTestDispatcher())
- @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+ @get:Rule val setFlagsRule = SetFlagsRule(flags)
private fun createDataProvider(
targetIntent: Intent,
@@ -54,16 +65,7 @@ class PreviewDataProviderTest {
additionalContentUri: Uri? = null,
resolver: ContentInterface = contentResolver,
typeClassifier: MimeTypeClassifier = mimeTypeClassifier,
- isPayloadTogglingEnabled: Boolean = false
- ) =
- PreviewDataProvider(
- scope,
- targetIntent,
- additionalContentUri,
- resolver,
- isPayloadTogglingEnabled,
- typeClassifier,
- )
+ ) = PreviewDataProvider(scope, targetIntent, additionalContentUri, resolver, typeClassifier)
@Test
fun test_nonSendIntentAction_resolvesToTextPreviewUiSynchronously() {
@@ -75,21 +77,49 @@ class PreviewDataProviderTest {
}
@Test
- fun test_sendSingleTextFileWithoutPreview_resolvesToFilePreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/notes.txt")
- val targetIntent =
- Intent(Intent.ACTION_SEND).apply {
- putExtra(Intent.EXTRA_STREAM, uri)
- type = "text/plain"
- }
- whenever(contentResolver.getType(uri)).thenReturn("text/plain")
- val testSubject = createDataProvider(targetIntent)
+ fun test_sendSingleTextFileWithoutPreview_resolvesToFilePreviewUi() =
+ testScope.runTest {
+ val fileName = "notes.txt"
+ val uri = Uri.parse("content://org.pkg.app/$fileName")
+ val targetIntent =
+ Intent(Intent.ACTION_SEND).apply {
+ putExtra(Intent.EXTRA_STREAM, uri)
+ type = "text/plain"
+ }
+ whenever(contentResolver.getType(uri)).thenReturn("text/plain")
+ val testSubject = createDataProvider(targetIntent)
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- assertThat(testSubject.uriCount).isEqualTo(1)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
- verify(contentResolver, times(1)).getType(any())
- }
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
+ assertThat(testSubject.uriCount).isEqualTo(1)
+ assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
+ assertThat(testSubject.getFirstFileName()).isEqualTo(fileName)
+ verify(contentResolver, times(1)).getType(any())
+ }
+
+ @Test
+ fun test_sendSingleTextFileWithDisplayNameAndTitle_displayNameTakesPrecedenceOverTitle() =
+ testScope.runTest {
+ val uri = Uri.parse("content://org.pkg.app/1234")
+ val targetIntent =
+ Intent(Intent.ACTION_SEND).apply {
+ putExtra(Intent.EXTRA_STREAM, uri)
+ type = "text/plain"
+ }
+ whenever(contentResolver.getType(uri)).thenReturn("text/plain")
+ val title = "Notes"
+ val displayName = "Notes.txt"
+ whenever(contentResolver.query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull()))
+ .thenReturn(
+ MatrixCursor(arrayOf(Downloads.Impl.COLUMN_TITLE, OpenableColumns.DISPLAY_NAME))
+ .apply { addRow(arrayOf(title, displayName)) }
+ )
+ contentResolver.setTitle(uri, title)
+ contentResolver.setDisplayName(uri, displayName)
+ val testSubject = createDataProvider(targetIntent)
+
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
+ assertThat(testSubject.getFirstFileName()).isEqualTo(displayName)
+ }
@Test
fun test_sendIntentWithoutUris_resolvesToTextPreviewUiSynchronously() {
@@ -115,60 +145,145 @@ class PreviewDataProviderTest {
}
@Test
- fun test_sendSingleNonImage_resolvesToFilePreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/paper.pdf")
- val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
- whenever(contentResolver.getType(uri)).thenReturn("application/pdf")
- val testSubject = createDataProvider(targetIntent)
+ fun test_sendSingleFile_resolvesToFilePreviewUi() =
+ testScope.runTest {
+ val fileName = "paper.pdf"
+ val uri = Uri.parse("content://org.pkg.app/$fileName")
+ val targetIntent =
+ Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
+ whenever(contentResolver.getType(uri)).thenReturn("application/pdf")
+ val testSubject = createDataProvider(targetIntent)
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- assertThat(testSubject.uriCount).isEqualTo(1)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
- assertThat(testSubject.firstFileInfo?.previewUri).isNull()
- verify(contentResolver, times(1)).getType(any())
- }
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
+ assertThat(testSubject.uriCount).isEqualTo(1)
+ assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
+ assertThat(testSubject.firstFileInfo?.previewUri).isNull()
+ assertThat(testSubject.getFirstFileName()).isEqualTo(fileName)
+ verify(contentResolver, times(1)).getType(any())
+ }
@Test
- fun test_sendSingleImageWithFailingGetType_resolvesToFilePreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/image.png")
- val targetIntent =
- Intent(Intent.ACTION_SEND).apply {
- type = "image/png"
- putExtra(Intent.EXTRA_STREAM, uri)
- }
- whenever(contentResolver.getType(uri)).thenThrow(SecurityException("test failure"))
- val testSubject = createDataProvider(targetIntent)
+ fun test_sendSingleImageWithFailingGetType_resolvesToFilePreviewUi() =
+ testScope.runTest {
+ val fileName = "image.png"
+ val uri = Uri.parse("content://org.pkg.app/$fileName")
+ val targetIntent =
+ Intent(Intent.ACTION_SEND).apply {
+ type = "image/png"
+ putExtra(Intent.EXTRA_STREAM, uri)
+ }
+ whenever(contentResolver.getType(uri)).thenThrow(SecurityException("test failure"))
+ val testSubject = createDataProvider(targetIntent)
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- assertThat(testSubject.uriCount).isEqualTo(1)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
- assertThat(testSubject.firstFileInfo?.previewUri).isNull()
- verify(contentResolver, times(1)).getType(any())
- }
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
+ assertThat(testSubject.uriCount).isEqualTo(1)
+ assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
+ assertThat(testSubject.firstFileInfo?.previewUri).isNull()
+ assertThat(testSubject.getFirstFileName()).isEqualTo(fileName)
+ verify(contentResolver, times(1)).getType(any())
+ }
@Test
- fun test_sendSingleImageWithFailingMetadata_resolvesToFilePreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/image.png")
- val targetIntent =
- Intent(Intent.ACTION_SEND).apply {
- type = "image/png"
- putExtra(Intent.EXTRA_STREAM, uri)
- }
- whenever(contentResolver.getStreamTypes(uri, "*/*"))
- .thenThrow(SecurityException("test failure"))
- whenever(contentResolver.query(uri, METADATA_COLUMNS, null, null))
- .thenThrow(SecurityException("test failure"))
- val testSubject = createDataProvider(targetIntent)
+ fun test_sendSingleFileWithFailingMetadata_resolvesToFilePreviewUi() =
+ testScope.runTest {
+ val fileName = "manual.pdf"
+ val uri = Uri.parse("content://org.pkg.app/$fileName")
+ val targetIntent =
+ Intent(Intent.ACTION_SEND).apply {
+ type = "application/pdf"
+ putExtra(Intent.EXTRA_STREAM, uri)
+ }
+ whenever(contentResolver.getType(uri)).thenReturn("application/pdf")
+ whenever(contentResolver.getStreamTypes(uri, "*/*"))
+ .thenThrow(SecurityException("test failure"))
+ whenever(contentResolver.query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull()))
+ .thenThrow(SecurityException("test failure"))
+ val testSubject = createDataProvider(targetIntent)
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- assertThat(testSubject.uriCount).isEqualTo(1)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
- assertThat(testSubject.firstFileInfo?.previewUri).isNull()
- verify(contentResolver, times(1)).getType(any())
- }
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
+ assertThat(testSubject.uriCount).isEqualTo(1)
+ assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
+ assertThat(testSubject.firstFileInfo?.previewUri).isNull()
+ assertThat(testSubject.getFirstFileName()).isEqualTo(fileName)
+ verify(contentResolver, times(1)).getType(any())
+ }
@Test
- fun test_SingleNonImageUriWithImageTypeInGetStreamTypes_useImagePreviewUi() {
+ @EnableFlags(FLAG_INDIVIDUAL_METADATA_TITLE_READ)
+ fun test_sendSingleImageWithFailingGetTypeDisjointTitleRead_resolvesToFilePreviewUi() =
+ testScope.runTest {
+ val uri = Uri.parse("content://org.pkg.app/image.png")
+ val targetIntent =
+ Intent(Intent.ACTION_SEND).apply {
+ type = "image/png"
+ putExtra(Intent.EXTRA_STREAM, uri)
+ }
+ whenever(contentResolver.getType(uri)).thenThrow(SecurityException("test failure"))
+ val title = "Image Title"
+ contentResolver.setTitle(uri, title)
+ val testSubject = createDataProvider(targetIntent)
+
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
+ assertThat(testSubject.uriCount).isEqualTo(1)
+ assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
+ assertThat(testSubject.firstFileInfo?.previewUri).isNull()
+ assertThat(testSubject.getFirstFileName()).isEqualTo(title)
+ verify(contentResolver, times(1)).getType(any())
+ }
+
+ @Test
+ fun test_sendSingleFileWithFailingImageMetadata_resolvesToFilePreviewUi() =
+ testScope.runTest {
+ val fileName = "notes.pdf"
+ val uri = Uri.parse("content://org.pkg.app/$fileName")
+ val targetIntent =
+ Intent(Intent.ACTION_SEND).apply {
+ type = "application/pdf"
+ putExtra(Intent.EXTRA_STREAM, uri)
+ }
+ whenever(contentResolver.getType(uri)).thenReturn("application/pdf")
+ whenever(contentResolver.getStreamTypes(uri, "*/*"))
+ .thenThrow(SecurityException("test failure"))
+ whenever(contentResolver.query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull()))
+ .thenThrow(SecurityException("test failure"))
+ val testSubject = createDataProvider(targetIntent)
+
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
+ assertThat(testSubject.uriCount).isEqualTo(1)
+ assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
+ assertThat(testSubject.firstFileInfo?.previewUri).isNull()
+ assertThat(testSubject.getFirstFileName()).isEqualTo(fileName)
+ verify(contentResolver, times(1)).getType(any())
+ }
+
+ @Test
+ @EnableFlags(FLAG_INDIVIDUAL_METADATA_TITLE_READ)
+ fun test_sendSingleFileWithFailingImageMetadataIndividualTitleRead_resolvesToFilePreviewUi() =
+ testScope.runTest {
+ val uri = Uri.parse("content://org.pkg.app/image.png")
+ val targetIntent =
+ Intent(Intent.ACTION_SEND).apply {
+ type = "image/png"
+ putExtra(Intent.EXTRA_STREAM, uri)
+ }
+ whenever(contentResolver.getStreamTypes(uri, "*/*"))
+ .thenThrow(SecurityException("test failure"))
+ whenever(contentResolver.query(uri, ICON_METADATA_COLUMNS, null, null))
+ .thenThrow(SecurityException("test failure"))
+ val displayName = "display name"
+ contentResolver.setDisplayName(uri, displayName)
+ val testSubject = createDataProvider(targetIntent)
+
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
+ assertThat(testSubject.uriCount).isEqualTo(1)
+ assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
+ assertThat(testSubject.firstFileInfo?.previewUri).isNull()
+ assertThat(testSubject.getFirstFileName()).isEqualTo(displayName)
+ verify(contentResolver, times(1)).getType(any())
+ }
+
+ @Test
+ fun test_SingleFileUriWithImageTypeInGetStreamTypes_useImagePreviewUi() {
val uri = Uri.parse("content://org.pkg.app/paper.pdf")
val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
whenever(contentResolver.getStreamTypes(uri, "*/*"))
@@ -190,7 +305,7 @@ class PreviewDataProviderTest {
arrayOf(
DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL or
DocumentsContract.Document.FLAG_SUPPORTS_METADATA
- )
+ ),
)
}
@@ -207,7 +322,8 @@ class PreviewDataProviderTest {
val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
whenever(contentResolver.getType(uri)).thenReturn("application/pdf")
val cursor = MatrixCursor(columns).apply { addRow(values) }
- whenever(contentResolver.query(uri, METADATA_COLUMNS, null, null)).thenReturn(cursor)
+ whenever(contentResolver.query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull()))
+ .thenReturn(cursor)
val testSubject = createDataProvider(targetIntent)
@@ -225,12 +341,13 @@ class PreviewDataProviderTest {
val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
whenever(contentResolver.getType(uri)).thenReturn("application/pdf")
val cursor = MatrixCursor(emptyArray())
- whenever(contentResolver.query(uri, METADATA_COLUMNS, null, null)).thenReturn(cursor)
+ whenever(contentResolver.query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull()))
+ .thenReturn(cursor)
val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- verify(contentResolver, times(1)).query(uri, METADATA_COLUMNS, null, null)
+ verify(contentResolver, times(1)).query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull())
assertThat(cursor.isClosed).isTrue()
}
@@ -245,7 +362,7 @@ class PreviewDataProviderTest {
ArrayList<Uri>().apply {
add(uri1)
add(uri2)
- }
+ },
)
}
whenever(contentResolver.getType(uri1)).thenReturn("image/png")
@@ -273,7 +390,7 @@ class PreviewDataProviderTest {
ArrayList<Uri>().apply {
add(uri1)
add(uri2)
- }
+ },
)
}
val testSubject = createDataProvider(targetIntent)
@@ -287,7 +404,7 @@ class PreviewDataProviderTest {
}
@Test
- fun test_someNonImageUriWithPreview_useImagePreviewUi() {
+ fun test_someFileUrisWithPreview_useImagePreviewUi() {
val uri1 = Uri.parse("content://org.pkg.app/test.mp4")
val uri2 = Uri.parse("content://org.pkg.app/test.pdf")
val targetIntent =
@@ -297,7 +414,7 @@ class PreviewDataProviderTest {
ArrayList<Uri>().apply {
add(uri1)
add(uri2)
- }
+ },
)
}
whenever(contentResolver.getType(uri1)).thenReturn("video/mpeg4")
@@ -313,29 +430,32 @@ class PreviewDataProviderTest {
}
@Test
- fun test_allNonImageUrisWithoutPreview_useFilePreviewUi() {
- val uri1 = Uri.parse("content://org.pkg.app/test.html")
- val uri2 = Uri.parse("content://org.pkg.app/test.pdf")
- val targetIntent =
- Intent(Intent.ACTION_SEND_MULTIPLE).apply {
- putExtra(
- Intent.EXTRA_STREAM,
- ArrayList<Uri>().apply {
- add(uri1)
- add(uri2)
- }
- )
- }
- whenever(contentResolver.getType(uri1)).thenReturn("text/html")
- whenever(contentResolver.getType(uri2)).thenReturn("application/pdf")
- val testSubject = createDataProvider(targetIntent)
+ fun test_allFileUrisWithoutPreview_useFilePreviewUi() =
+ testScope.runTest {
+ val firstFileName = "test.html"
+ val uri1 = Uri.parse("content://org.pkg.app/$firstFileName")
+ val uri2 = Uri.parse("content://org.pkg.app/test.pdf")
+ val targetIntent =
+ Intent(Intent.ACTION_SEND_MULTIPLE).apply {
+ putExtra(
+ Intent.EXTRA_STREAM,
+ ArrayList<Uri>().apply {
+ add(uri1)
+ add(uri2)
+ },
+ )
+ }
+ whenever(contentResolver.getType(uri1)).thenReturn("text/html")
+ whenever(contentResolver.getType(uri2)).thenReturn("application/pdf")
+ val testSubject = createDataProvider(targetIntent)
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- assertThat(testSubject.uriCount).isEqualTo(2)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri1)
- assertThat(testSubject.firstFileInfo?.previewUri).isNull()
- verify(contentResolver, times(2)).getType(any())
- }
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
+ assertThat(testSubject.uriCount).isEqualTo(2)
+ assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri1)
+ assertThat(testSubject.firstFileInfo?.previewUri).isNull()
+ assertThat(testSubject.getFirstFileName()).isEqualTo(firstFileName)
+ verify(contentResolver, times(2)).getType(any())
+ }
@Test
fun test_imagePreviewFileInfoFlow_dataLoadedOnce() =
@@ -349,7 +469,7 @@ class PreviewDataProviderTest {
ArrayList<Uri>().apply {
add(uri1)
add(uri2)
- }
+ },
)
}
whenever(contentResolver.getType(uri1)).thenReturn("text/html")
@@ -373,7 +493,8 @@ class PreviewDataProviderTest {
}
@Test
- fun sendItemsWithAdditionalContentUri_showPayloadTogglingUi() {
+ @EnableFlags(FLAG_CHOOSER_PAYLOAD_TOGGLING)
+ fun sendImageWithAdditionalContentUri_showPayloadTogglingUi() {
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")
@@ -381,7 +502,6 @@ class PreviewDataProviderTest {
createDataProvider(
targetIntent,
additionalContentUri = Uri.parse("content://org.pkg.app.extracontent"),
- isPayloadTogglingEnabled = true,
)
assertThat(testSubject.previewType)
@@ -393,7 +513,8 @@ class PreviewDataProviderTest {
}
@Test
- fun sendItemsWithAdditionalContentUri_showImagePreviewUi() {
+ @DisableFlags(FLAG_CHOOSER_PAYLOAD_TOGGLING)
+ fun sendImageWithAdditionalContentUriAndDisabledFlag_showImagePreviewUi() {
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")
@@ -411,6 +532,7 @@ class PreviewDataProviderTest {
}
@Test
+ @EnableFlags(FLAG_CHOOSER_PAYLOAD_TOGGLING)
fun sendItemsWithAdditionalContentUriWithSameAuthority_showImagePreviewUi() {
val uri = Uri.parse("content://org.pkg.app/image.png")
val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
@@ -419,7 +541,6 @@ class PreviewDataProviderTest {
createDataProvider(
targetIntent,
additionalContentUri = Uri.parse("content://org.pkg.app/extracontent"),
- isPayloadTogglingEnabled = true,
)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
@@ -435,10 +556,28 @@ class PreviewDataProviderTest {
val testSubject =
createDataProvider(
targetIntent,
- additionalContentUri = Uri.parse("content://org.pkg.app/extracontent")
+ additionalContentUri = Uri.parse("content://org.pkg.app/extracontent"),
)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT)
verify(contentResolver, never()).getType(any())
}
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun parameters(): List<FlagsParameterization> =
+ FlagsParameterization.allCombinationsOf(FLAG_INDIVIDUAL_METADATA_TITLE_READ)
+ }
+}
+
+private fun ContentInterface.setDisplayName(uri: Uri, displayName: String) =
+ setMetadata(uri, arrayOf(OpenableColumns.DISPLAY_NAME), arrayOf(displayName))
+
+private fun ContentInterface.setTitle(uri: Uri, title: String) =
+ setMetadata(uri, arrayOf(Downloads.Impl.COLUMN_TITLE), arrayOf(title))
+
+private fun ContentInterface.setMetadata(uri: Uri, columns: Array<String>, values: Array<String>) {
+ whenever(query(uri, columns, null, null))
+ .thenReturn(MatrixCursor(columns).apply { addRow(values) })
}
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/PreviewImageLoaderTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/PreviewImageLoaderTest.kt
new file mode 100644
index 00000000..8c810058
--- /dev/null
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/PreviewImageLoaderTest.kt
@@ -0,0 +1,496 @@
+/*
+ * 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.graphics.Bitmap
+import android.net.Uri
+import android.util.Size
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.atomic.AtomicInteger
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class PreviewImageLoaderTest {
+ private val scope = TestScope()
+
+ @Test
+ fun test_cachingImageRequest_imageCached() =
+ scope.runTest {
+ val uri = createUri(0)
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uri] = { size -> createBitmap(size.width, size.height) }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ val b1 = testSubject.invoke(uri, Size(200, 100))
+ val b2 = testSubject.invoke(uri, Size(200, 100), caching = false)
+ assertThat(b1).isEqualTo(b2)
+ assertThat(thumbnailLoader.invokeCalls).hasSize(1)
+ }
+
+ @Test
+ fun test_nonCachingImageRequest_imageNotCached() =
+ scope.runTest {
+ val uri = createUri(0)
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uri] = { size -> createBitmap(size.width, size.height) }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ testSubject.invoke(uri, Size(200, 100), caching = false)
+ testSubject.invoke(uri, Size(200, 100), caching = false)
+ assertThat(thumbnailLoader.invokeCalls).hasSize(2)
+ }
+
+ @Test
+ fun test_twoSimultaneousImageRequests_requestsDeduplicated() =
+ scope.runTest {
+ val uri = createUri(0)
+ val loadingStartedDeferred = CompletableDeferred<Unit>()
+ val bitmapDeferred = CompletableDeferred<Bitmap>()
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uri] = {
+ loadingStartedDeferred.complete(Unit)
+ bitmapDeferred.await()
+ }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ val b1Deferred = async { testSubject.invoke(uri, Size(200, 100), caching = false) }
+ loadingStartedDeferred.await()
+ val b2Deferred =
+ async(start = CoroutineStart.UNDISPATCHED) {
+ testSubject.invoke(uri, Size(200, 100), caching = true)
+ }
+ bitmapDeferred.complete(createBitmap(200, 200))
+
+ val b1 = b1Deferred.await()
+ val b2 = b2Deferred.await()
+ assertThat(b1).isEqualTo(b2)
+ assertThat(thumbnailLoader.invokeCalls).hasSize(1)
+ }
+
+ @Test
+ fun test_cachingRequestCancelledAndEvoked_imageLoadingCancelled() =
+ scope.runTest {
+ val uriOne = createUri(1)
+ val uriTwo = createUri(2)
+ val loadingStartedDeferred = CompletableDeferred<Unit>()
+ val cancelledRequests = mutableSetOf<Uri>()
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uriOne] = {
+ loadingStartedDeferred.complete(Unit)
+ try {
+ awaitCancellation()
+ } catch (e: CancellationException) {
+ cancelledRequests.add(uriOne)
+ throw e
+ }
+ }
+ fakeInvoke[uriTwo] = { createBitmap(200, 200) }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ cacheSize = 1,
+ defaultPreviewSize = 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ val jobOne = launch { testSubject.invoke(uriOne, Size(200, 100)) }
+ loadingStartedDeferred.await()
+ jobOne.cancel()
+ scope.runCurrent()
+
+ assertThat(cancelledRequests).isEmpty()
+
+ // second URI should evict the first item from the cache
+ testSubject.invoke(uriTwo, Size(200, 100))
+
+ assertThat(thumbnailLoader.invokeCalls).hasSize(2)
+ assertThat(cancelledRequests).containsExactly(uriOne)
+ }
+
+ @Test
+ fun test_nonCachingRequestClientCancels_imageLoadingCancelled() =
+ scope.runTest {
+ val uri = createUri(1)
+ val loadingStartedDeferred = CompletableDeferred<Unit>()
+ val cancelledRequests = mutableSetOf<Uri>()
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uri] = {
+ loadingStartedDeferred.complete(Unit)
+ try {
+ awaitCancellation()
+ } catch (e: CancellationException) {
+ cancelledRequests.add(uri)
+ throw e
+ }
+ }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ cacheSize = 1,
+ defaultPreviewSize = 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ val job = launch { testSubject.invoke(uri, Size(200, 100), caching = false) }
+ loadingStartedDeferred.await()
+ job.cancel()
+ scope.runCurrent()
+
+ assertThat(cancelledRequests).containsExactly(uri)
+ }
+
+ @Test
+ fun test_requestHigherResImage_newImageLoaded() =
+ scope.runTest {
+ val uri = createUri(0)
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uri] = { size -> createBitmap(size.width, size.height) }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ val b1 = testSubject.invoke(uri, Size(100, 100))
+ val b2 = testSubject.invoke(uri, Size(200, 200))
+ assertThat(b1).isNotNull()
+ assertThat(b1!!.width).isEqualTo(100)
+ assertThat(b2).isNotNull()
+ assertThat(b2!!.width).isEqualTo(200)
+ assertThat(thumbnailLoader.invokeCalls).hasSize(2)
+ }
+
+ @Test
+ fun test_imageLoadingThrowsException_returnsNull() =
+ scope.runTest {
+ val uri = createUri(0)
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uri] = { throw SecurityException("test") }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ val bitmap = testSubject.invoke(uri, Size(100, 100))
+ assertThat(bitmap).isNull()
+ }
+
+ @Test
+ fun test_requestHigherResImage_cancelsLowerResLoading() =
+ scope.runTest {
+ val uri = createUri(0)
+ val cancelledRequestCount = AtomicInteger(0)
+ val imageLoadingStarted = CompletableDeferred<Unit>()
+ val bitmapDeferred = CompletableDeferred<Bitmap>()
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uri] = {
+ imageLoadingStarted.complete(Unit)
+ try {
+ bitmapDeferred.await()
+ } catch (e: CancellationException) {
+ cancelledRequestCount.getAndIncrement()
+ throw e
+ }
+ }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ val lowResSize = 100
+ val highResSize = 200
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ testSubject.invoke(uri, Size(lowResSize, lowResSize))
+ }
+ imageLoadingStarted.await()
+ val result = async { testSubject.invoke(uri, Size(highResSize, highResSize)) }
+ runCurrent()
+ assertThat(cancelledRequestCount.get()).isEqualTo(1)
+
+ bitmapDeferred.complete(createBitmap(highResSize, highResSize))
+ val bitmap = result.await()
+ assertThat(bitmap).isNotNull()
+ assertThat(bitmap!!.width).isEqualTo(highResSize)
+ assertThat(thumbnailLoader.invokeCalls).hasSize(2)
+ }
+
+ @Test
+ fun test_requestLowerResImage_cachedHigherResImageReturned() =
+ scope.runTest {
+ val uri = createUri(0)
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uri] = { size -> createBitmap(size.width, size.height) }
+ }
+ val lowResSize = 100
+ val highResSize = 200
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ val b1 = testSubject.invoke(uri, Size(highResSize, highResSize))
+ val b2 = testSubject.invoke(uri, Size(lowResSize, lowResSize))
+ assertThat(b1).isEqualTo(b2)
+ assertThat(b2!!.width).isEqualTo(highResSize)
+ assertThat(thumbnailLoader.invokeCalls).hasSize(1)
+ }
+
+ @Test
+ fun test_incorrectSizeRequested_defaultSizeIsUsed() =
+ scope.runTest {
+ val uri = createUri(0)
+ val defaultPreviewSize = 100
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uri] = { size -> createBitmap(size.width, size.height) }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ cacheSize = 1,
+ defaultPreviewSize,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ val b1 = testSubject(uri, Size(0, 0))
+ assertThat(b1!!.width).isEqualTo(defaultPreviewSize)
+
+ val largerImageSize = 200
+ val b2 = testSubject(uri, Size(largerImageSize, largerImageSize))
+ assertThat(b2!!.width).isEqualTo(largerImageSize)
+ }
+
+ @Test
+ fun test_prePopulateImages_cachesImagesUpToTheCacheSize() =
+ scope.runTest {
+ val previewSize = Size(100, 100)
+ val uris = List(2) { createUri(it) }
+ val loadingCount = AtomicInteger(0)
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ for (uri in uris) {
+ fakeInvoke[uri] = { size ->
+ loadingCount.getAndIncrement()
+ createBitmap(size.width, size.height)
+ }
+ }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ testSubject.prePopulate(uris.map { it to previewSize })
+ runCurrent()
+
+ assertThat(loadingCount.get()).isEqualTo(1)
+ assertThat(thumbnailLoader.invokeCalls).containsExactly(uris[0])
+
+ testSubject(uris[0], previewSize)
+ runCurrent()
+
+ assertThat(loadingCount.get()).isEqualTo(1)
+ }
+
+ @Test
+ fun test_oldRecordEvictedFromTheCache() =
+ scope.runTest {
+ val previewSize = Size(100, 100)
+ val uriOne = createUri(1)
+ val uriTwo = createUri(2)
+ val requestsPerUri = HashMap<Uri, AtomicInteger>()
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ for (uri in arrayOf(uriOne, uriTwo)) {
+ fakeInvoke[uri] = { size ->
+ requestsPerUri.getOrPut(uri) { AtomicInteger() }.incrementAndGet()
+ createBitmap(size.width, size.height)
+ }
+ }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ testSubject(uriOne, previewSize)
+ testSubject(uriTwo, previewSize)
+ testSubject(uriTwo, previewSize)
+ testSubject(uriOne, previewSize)
+
+ assertThat(requestsPerUri[uriOne]?.get()).isEqualTo(2)
+ assertThat(requestsPerUri[uriTwo]?.get()).isEqualTo(1)
+ }
+
+ @Test
+ fun test_doNotCacheNulls() =
+ scope.runTest {
+ val previewSize = Size(100, 100)
+ val uri = createUri(1)
+ val loadingCount = AtomicInteger(0)
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uri] = {
+ loadingCount.getAndIncrement()
+ null
+ }
+ }
+ val testSubject =
+ PreviewImageLoader(
+ backgroundScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ testSubject(uri, previewSize)
+ testSubject(uri, previewSize)
+
+ assertThat(loadingCount.get()).isEqualTo(2)
+ }
+
+ @Test(expected = CancellationException::class)
+ fun invoke_onClosedImageLoaderScope_throwsCancellationException() =
+ scope.runTest {
+ val uri = createUri(1)
+ val thumbnailLoader = FakeThumbnailLoader().apply { fakeInvoke[uri] = { null } }
+ val imageLoaderScope = CoroutineScope(coroutineContext)
+ val testSubject =
+ PreviewImageLoader(
+ imageLoaderScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+ imageLoaderScope.cancel()
+ testSubject(uri, Size(200, 200))
+ }
+
+ @Test(expected = CancellationException::class)
+ fun invoke_imageLoaderScopeClosedMidflight_throwsCancellationException() =
+ scope.runTest {
+ val uri = createUri(1)
+ val loadingStarted = CompletableDeferred<Unit>()
+ val bitmapDeferred = CompletableDeferred<Bitmap?>()
+ val thumbnailLoader =
+ FakeThumbnailLoader().apply {
+ fakeInvoke[uri] = {
+ loadingStarted.complete(Unit)
+ bitmapDeferred.await()
+ }
+ }
+ val imageLoaderScope = CoroutineScope(coroutineContext)
+ val testSubject =
+ PreviewImageLoader(
+ imageLoaderScope,
+ 1,
+ 100,
+ thumbnailLoader,
+ StandardTestDispatcher(scope.testScheduler),
+ )
+
+ launch {
+ loadingStarted.await()
+ imageLoaderScope.cancel()
+ }
+ testSubject(uri, Size(200, 200))
+ }
+}
+
+private fun createUri(id: Int) = Uri.parse("content://org.pkg.app/image-$id.png")
+
+private fun createBitmap(width: Int, height: Int) =
+ Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/cursor/PayloadToggleCursorResolverTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/cursor/PayloadToggleCursorResolverTest.kt
index 5d81ec2a..f0813623 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/cursor/PayloadToggleCursorResolverTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/cursor/PayloadToggleCursorResolverTest.kt
@@ -30,9 +30,12 @@ import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.capture
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
class PayloadToggleCursorResolverTest {
private val cursorUri = Uri.parse("content://org.pkg.app.extra")
@@ -101,6 +104,9 @@ class PayloadToggleCursorResolverTest {
assertThat(row!!.uri).isEqualTo(uri)
assertThat(row.previewSize).isEqualTo(Size(100, 50))
}
+ val columnsCaptor = argumentCaptor<Array<String>>()
+ verify(fakeContentProvider).query(eq(cursorUri), columnsCaptor.capture(), any(), any())
+ assertThat(columnsCaptor.firstValue.toList()).containsExactly(URI, WIDTH, HEIGHT)
}
@Test
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractorTest.kt
index 48e43190..c4ba8105 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractorTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractorTest.kt
@@ -18,10 +18,13 @@
package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor
+import android.database.Cursor
import android.database.MatrixCursor
import android.net.Uri
import android.provider.MediaStore.MediaColumns.HEIGHT
import android.provider.MediaStore.MediaColumns.WIDTH
+import android.service.chooser.AdditionalContentContract.Columns.URI
+import android.service.chooser.AdditionalContentContract.CursorExtraKeys.POSITION
import android.util.Size
import androidx.core.os.bundleOf
import com.android.intentresolver.contentpreview.FileInfo
@@ -39,6 +42,7 @@ import com.android.intentresolver.util.cursor.CursorView
import com.android.intentresolver.util.cursor.viewBy
import com.android.intentresolver.util.runTest
import com.android.systemui.kosmos.Kosmos
+import com.google.common.truth.Correspondence
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
@@ -93,9 +97,9 @@ class CursorPreviewsInteractorTest {
private val cursorSizes: Map<Int, Size>,
) {
val cursor: CursorView<CursorRow?> =
- MatrixCursor(arrayOf("uri", WIDTH, HEIGHT))
+ MatrixCursor(arrayOf(URI, WIDTH, HEIGHT))
.apply {
- extras = bundleOf("position" to cursorStartPosition)
+ extras = bundleOf(POSITION to cursorStartPosition)
for (i in cursorRange) {
val size = cursorSizes[i]
addRow(
@@ -279,22 +283,83 @@ class CursorPreviewsInteractorTest {
) { deps ->
previewSelectionsRepository.selections.value =
PreviewModel(
- uri = uri(1),
- mimeType = "image/png",
- order = 0,
- ).let { mapOf(it.uri to it) }
+ uri = uri(1),
+ mimeType = "image/png",
+ order = 0,
+ )
+ .let { mapOf(it.uri to it) }
backgroundScope.launch {
cursorPreviewsInteractor.launch(deps.cursor, deps.initialPreviews)
}
runCurrent()
- assertThat(previewSelectionsRepository.selections.value.values).containsExactly(
- PreviewModel(
- uri = uri(1),
- mimeType = "image/bitmap",
- order = 1,
+ assertThat(previewSelectionsRepository.selections.value.values)
+ .containsExactly(
+ PreviewModel(
+ uri = uri(1),
+ mimeType = "image/bitmap",
+ order = 1,
+ )
+ )
+ }
+
+ @Test
+ fun testReadFailedPages() =
+ runTestWithDeps(
+ initialSelection = listOf(4),
+ cursor = emptyList(),
+ cursorStartPosition = 0,
+ pageSize = 2,
+ maxLoadedPages = 5,
+ ) { deps ->
+ val cursor =
+ MatrixCursor(arrayOf(URI)).apply {
+ extras = bundleOf(POSITION to 4)
+ for (i in 0 until 10) {
+ addRow(arrayOf(uri(i)))
+ }
+ }
+ val failingPositions = setOf(1, 5, 8)
+ val failingCursor =
+ object : Cursor by cursor {
+ override fun move(offset: Int): Boolean = moveToPosition(position + offset)
+
+ override fun moveToPosition(position: Int): Boolean {
+ if (failingPositions.contains(position)) {
+ throw RuntimeException(
+ "A test exception when moving the cursor to position $position"
+ )
+ }
+ return cursor.moveToPosition(position)
+ }
+
+ override fun moveToFirst(): Boolean = moveToPosition(0)
+
+ override fun moveToLast(): Boolean = moveToPosition(count - 1)
+
+ override fun moveToNext(): Boolean = move(1)
+
+ override fun moveToPrevious(): Boolean = move(-1)
+ }
+ .viewBy {
+ getString(0)?.let { uriStr ->
+ CursorRow(Uri.parse(uriStr), readSize(), position)
+ }
+ }
+ backgroundScope.launch {
+ cursorPreviewsInteractor.launch(failingCursor, deps.initialPreviews)
+ }
+ runCurrent()
+
+ assertThat(cursorPreviewsRepository.previewsModel.value).isNotNull()
+ assertThat(cursorPreviewsRepository.previewsModel.value!!.previewModels)
+ .comparingElementsUsing<PreviewModel, Uri>(
+ Correspondence.transforming({ it.uri }, "has a Uri of")
+ )
+ .containsExactlyElementsIn(
+ (0..7).filterNot { failingPositions.contains(it) }.map { uri(it) }
)
- )
+ .inOrder()
}
}
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractorTest.kt
index f329b8a7..5d9ddbb6 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractorTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractorTest.kt
@@ -26,13 +26,16 @@ import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.Tar
import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.targetIntentModifier
import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
import com.android.intentresolver.data.repository.chooserRequestRepository
+import com.android.intentresolver.logging.FakeEventLog
import com.android.intentresolver.util.runKosmosTest
+import com.android.internal.logging.InstanceId
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
import org.junit.Test
class SelectablePreviewInteractorTest {
+ private val eventLog = FakeEventLog(InstanceId.fakeInstanceId(0))
@Test
fun reflectPreviewRepo_initState() = runKosmosTest {
@@ -46,6 +49,7 @@ class SelectablePreviewInteractorTest {
order = 0,
),
selectionInteractor = selectionInteractor,
+ eventLog = eventLog,
)
runCurrent()
@@ -64,6 +68,7 @@ class SelectablePreviewInteractorTest {
order = 0,
),
selectionInteractor = selectionInteractor,
+ eventLog = eventLog,
)
assertThat(underTest.isSelected.first()).isFalse()
@@ -93,6 +98,7 @@ class SelectablePreviewInteractorTest {
order = 0,
),
selectionInteractor = selectionInteractor,
+ eventLog = eventLog,
)
underTest.setSelected(true)
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractorTest.kt
index 87db243d..c8242333 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractorTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractorTest.kt
@@ -18,16 +18,24 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interacto
import android.content.Intent
import android.net.Uri
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.intentresolver.Flags
import com.android.intentresolver.contentpreview.mimetypeClassifier
import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.previewSelectionsRepository
import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
import com.android.intentresolver.util.runKosmosTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
+import org.junit.Rule
import org.junit.Test
class SelectionInteractorTest {
+ @get:Rule val flagsRule = SetFlagsRule()
+
@Test
+ @DisableFlags(Flags.FLAG_UNSELECT_FINAL_ITEM)
fun singleSelection_removalPrevented() = runKosmosTest {
val initialPreview =
PreviewModel(
@@ -54,6 +62,33 @@ class SelectionInteractorTest {
}
@Test
+ @EnableFlags(Flags.FLAG_UNSELECT_FINAL_ITEM)
+ fun singleSelection_itemRemovedNoPendingIntentUpdates() = runKosmosTest {
+ val initialPreview =
+ PreviewModel(
+ uri = Uri.fromParts("scheme", "ssp", "fragment"),
+ mimeType = null,
+ order = 0
+ )
+ previewSelectionsRepository.selections.value = mapOf(initialPreview.uri to initialPreview)
+
+ val underTest =
+ SelectionInteractor(
+ previewSelectionsRepository,
+ { Intent() },
+ updateTargetIntentInteractor,
+ mimetypeClassifier,
+ )
+
+ assertThat(underTest.selections.first()).containsExactly(initialPreview.uri)
+
+ underTest.unselect(initialPreview)
+
+ assertThat(underTest.selections.first()).isEmpty()
+ assertThat(previewSelectionsRepository.selections.value).isEmpty()
+ }
+
+ @Test
fun multipleSelections_removalAllowed() = runKosmosTest {
val first =
PreviewModel(
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractorTest.kt
index 570c346c..32d040fe 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractorTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractorTest.kt
@@ -18,7 +18,11 @@
package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor
+import android.content.ComponentName
import android.content.Intent
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.intentresolver.Flags.FLAG_SHAREOUSEL_UPDATE_EXCLUDE_COMPONENTS_EXTRA
import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.pendingSelectionCallbackRepository
import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate
import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ValueUpdate
@@ -29,9 +33,12 @@ import com.android.intentresolver.util.runKosmosTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
+import org.junit.Rule
import org.junit.Test
class UpdateChooserRequestInteractorTest {
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
@Test
fun updateTargetIntentWithSelection() = runKosmosTest {
val selectionCallbackResult = ShareouselUpdate(metadataText = ValueUpdate.Value("update"))
@@ -45,4 +52,21 @@ class UpdateChooserRequestInteractorTest {
assertThat(pendingSelectionCallbackRepository.pendingTargetIntent.value).isNull()
assertThat(chooserRequestRepository.chooserRequest.value.metadataText).isEqualTo("update")
}
+
+ @Test
+ @EnableFlags(FLAG_SHAREOUSEL_UPDATE_EXCLUDE_COMPONENTS_EXTRA)
+ fun testSelectionResultWithExcludedComponents_chooserRequestIsUpdated() = runKosmosTest {
+ val excludedComponent = ComponentName("org.pkg.app", "Class")
+ val selectionCallbackResult =
+ ShareouselUpdate(excludeComponents = ValueUpdate.Value(listOf(excludedComponent)))
+ selectionChangeCallback = SelectionChangeCallback { selectionCallbackResult }
+
+ backgroundScope.launch { processTargetIntentUpdatesInteractor.activate() }
+
+ updateTargetIntentInteractor.updateTargetIntent(Intent())
+ runCurrent()
+
+ assertThat(chooserRequestRepository.chooserRequest.value.filteredComponentNames)
+ .containsExactly(excludedComponent)
+ }
}
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackImplTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackImplTest.kt
index 91bbd151..c1a1833a 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackImplTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallbackImplTest.kt
@@ -29,32 +29,34 @@ import android.content.Intent.EXTRA_CHOOSER_MODIFY_SHARE_ACTION
import android.content.Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER
import android.content.Intent.EXTRA_CHOOSER_RESULT_INTENT_SENDER
import android.content.Intent.EXTRA_CHOOSER_TARGETS
+import android.content.Intent.EXTRA_EXCLUDE_COMPONENTS
import android.content.Intent.EXTRA_INTENT
import android.content.Intent.EXTRA_METADATA_TEXT
import android.content.Intent.EXTRA_STREAM
import android.graphics.drawable.Icon
import android.net.Uri
import android.os.Bundle
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.service.chooser.AdditionalContentContract.MethodNames.ON_SELECTION_CHANGED
import android.service.chooser.ChooserAction
import android.service.chooser.ChooserTarget
-import android.service.chooser.Flags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.intentresolver.Flags.FLAG_SHAREOUSEL_UPDATE_EXCLUDE_COMPONENTS_EXTRA
import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ValueUpdate
import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ValueUpdate.Absent
-import com.android.intentresolver.inject.FakeChooserServiceFlags
import com.google.common.truth.Correspondence
import com.google.common.truth.Correspondence.BinaryPredicate
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import java.lang.IllegalArgumentException
import kotlinx.coroutines.test.runTest
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
-import org.mockito.kotlin.capture
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
@@ -62,20 +64,16 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class SelectionChangeCallbackImplTest {
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
private val uri = Uri.parse("content://org.pkg/content-provider")
private val chooserIntent = Intent(ACTION_CHOOSER)
private val contentResolver = mock<ContentInterface>()
private val context = InstrumentationRegistry.getInstrumentation().context
- private val flags =
- FakeChooserServiceFlags().apply {
- setFlag(Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING, false)
- setFlag(Flags.FLAG_CHOOSER_ALBUM_TEXT, false)
- setFlag(Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA, false)
- }
@Test
fun testPayloadChangeCallbackContact() = runTest {
- val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver, flags)
+ val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver)
val u1 = createUri(1)
val u2 = createUri(2)
@@ -170,7 +168,7 @@ class SelectionChangeCallbackImplTest {
Bundle().apply { putParcelableArray(EXTRA_CHOOSER_CUSTOM_ACTIONS, arrayOf(a1, a2)) }
)
- val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver, flags)
+ val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver)
val targetIntent = Intent(ACTION_SEND_MULTIPLE)
val result = testSubject.onSelectionChanged(targetIntent)
@@ -187,6 +185,7 @@ class SelectionChangeCallbackImplTest {
assertThat(result.refinementIntentSender).isEqualTo(Absent)
assertThat(result.resultIntentSender).isEqualTo(Absent)
assertThat(result.metadataText).isEqualTo(Absent)
+ assertThat(result.excludeComponents).isEqualTo(Absent)
}
@Test
@@ -208,7 +207,7 @@ class SelectionChangeCallbackImplTest {
Bundle().apply { putParcelable(EXTRA_CHOOSER_MODIFY_SHARE_ACTION, modifyShare) }
)
- val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver, flags)
+ val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver)
val targetIntent = Intent(ACTION_SEND)
val result = testSubject.onSelectionChanged(targetIntent)
@@ -227,6 +226,7 @@ class SelectionChangeCallbackImplTest {
assertThat(result.refinementIntentSender).isEqualTo(Absent)
assertThat(result.resultIntentSender).isEqualTo(Absent)
assertThat(result.metadataText).isEqualTo(Absent)
+ assertThat(result.excludeComponents).isEqualTo(Absent)
}
@Test
@@ -243,7 +243,7 @@ class SelectionChangeCallbackImplTest {
Bundle().apply { putParcelableArray(EXTRA_ALTERNATE_INTENTS, alternateIntents) }
)
- val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver, flags)
+ val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver)
val targetIntent = Intent(ACTION_SEND)
val result = testSubject.onSelectionChanged(targetIntent)
@@ -268,6 +268,7 @@ class SelectionChangeCallbackImplTest {
assertThat(result.refinementIntentSender).isEqualTo(Absent)
assertThat(result.resultIntentSender).isEqualTo(Absent)
assertThat(result.metadataText).isEqualTo(Absent)
+ assertThat(result.excludeComponents).isEqualTo(Absent)
}
@Test
@@ -293,7 +294,7 @@ class SelectionChangeCallbackImplTest {
Bundle().apply { putParcelableArray(EXTRA_CHOOSER_TARGETS, arrayOf(t1, t2)) }
)
- val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver, flags)
+ val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver)
val targetIntent = Intent(ACTION_SEND)
val result = testSubject.onSelectionChanged(targetIntent)
@@ -321,6 +322,7 @@ class SelectionChangeCallbackImplTest {
assertThat(result.refinementIntentSender).isEqualTo(Absent)
assertThat(result.resultIntentSender).isEqualTo(Absent)
assertThat(result.metadataText).isEqualTo(Absent)
+ assertThat(result.excludeComponents).isEqualTo(Absent)
}
@Test
@@ -335,7 +337,7 @@ class SelectionChangeCallbackImplTest {
}
)
- val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver, flags)
+ val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver)
val targetIntent = Intent(ACTION_SEND)
val result = testSubject.onSelectionChanged(targetIntent)
@@ -348,6 +350,7 @@ class SelectionChangeCallbackImplTest {
assertThat(result.refinementIntentSender.getOrThrow()).isNotNull()
assertThat(result.resultIntentSender).isEqualTo(Absent)
assertThat(result.metadataText).isEqualTo(Absent)
+ assertThat(result.excludeComponents).isEqualTo(Absent)
}
@Test
@@ -362,7 +365,7 @@ class SelectionChangeCallbackImplTest {
}
)
- val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver, flags)
+ val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver)
val targetIntent = Intent(ACTION_SEND)
val result = testSubject.onSelectionChanged(targetIntent)
@@ -375,15 +378,16 @@ class SelectionChangeCallbackImplTest {
assertThat(result.refinementIntentSender).isEqualTo(Absent)
assertThat(result.resultIntentSender.getOrThrow()).isNotNull()
assertThat(result.metadataText).isEqualTo(Absent)
+ assertThat(result.excludeComponents).isEqualTo(Absent)
}
@Test
- fun testPayloadChangeCallbackUpdatesMetadataTextWithDisabledFlag_noUpdates() = runTest {
+ fun testPayloadChangeCallbackUpdatesMetadataTextWithEnabledFlag_valueUpdated() = runTest {
val metadataText = "[Metadata]"
whenever(contentResolver.call(any<String>(), any(), any(), any()))
.thenReturn(Bundle().apply { putCharSequence(EXTRA_METADATA_TEXT, metadataText) })
- val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver, flags)
+ val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver)
val targetIntent = Intent(ACTION_SEND)
val result = testSubject.onSelectionChanged(targetIntent)
@@ -395,20 +399,26 @@ class SelectionChangeCallbackImplTest {
assertThat(result.callerTargets).isEqualTo(Absent)
assertThat(result.refinementIntentSender).isEqualTo(Absent)
assertThat(result.resultIntentSender).isEqualTo(Absent)
- assertThat(result.metadataText).isEqualTo(Absent)
+ assertThat(result.metadataText.getOrThrow()).isEqualTo(metadataText)
+ assertThat(result.excludeComponents).isEqualTo(Absent)
}
@Test
- fun testPayloadChangeCallbackUpdatesMetadataTextWithEnabledFlag_valueUpdated() = runTest {
- val metadataText = "[Metadata]"
- flags.setFlag(Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA, true)
+ @EnableFlags(FLAG_SHAREOUSEL_UPDATE_EXCLUDE_COMPONENTS_EXTRA)
+ fun testPayloadChangeCallbackUpdatesExcludedComponents_valueUpdated() = runTest {
+ val excludedComponent = ComponentName("org.pkg.app", "org.pkg.app.TheClass")
whenever(contentResolver.call(any<String>(), any(), any(), any()))
- .thenReturn(Bundle().apply { putCharSequence(EXTRA_METADATA_TEXT, metadataText) })
+ .thenReturn(
+ Bundle().apply {
+ putParcelableArray(EXTRA_EXCLUDE_COMPONENTS, arrayOf(excludedComponent))
+ }
+ )
- val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver, flags)
+ val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver)
val targetIntent = Intent(ACTION_SEND)
val result = testSubject.onSelectionChanged(targetIntent)
+
assertWithMessage("Callback result should not be null").that(result).isNotNull()
requireNotNull(result)
assertThat(result.customActions).isEqualTo(Absent)
@@ -417,12 +427,12 @@ class SelectionChangeCallbackImplTest {
assertThat(result.callerTargets).isEqualTo(Absent)
assertThat(result.refinementIntentSender).isEqualTo(Absent)
assertThat(result.resultIntentSender).isEqualTo(Absent)
- assertThat(result.metadataText.getOrThrow()).isEqualTo(metadataText)
+ assertThat(result.metadataText).isEqualTo(Absent)
+ assertThat(result.excludeComponents.getOrThrow()).containsExactly(excludedComponent)
}
@Test
fun testPayloadChangeCallbackProvidesInvalidData_invalidDataIgnored() = runTest {
- flags.setFlag(Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA, true)
whenever(contentResolver.call(any<String>(), any(), any(), any()))
.thenReturn(
Bundle().apply {
@@ -436,7 +446,7 @@ class SelectionChangeCallbackImplTest {
}
)
- val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver, flags)
+ val testSubject = SelectionChangeCallbackImpl(uri, chooserIntent, contentResolver)
val targetIntent = Intent(ACTION_SEND)
val result = testSubject.onSelectionChanged(targetIntent)
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModelTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModelTest.kt
index bb67e084..fc7ac751 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModelTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModelTest.kt
@@ -76,23 +76,25 @@ class ShareouselViewModelTest {
scope = viewModelScope,
)
}
+ private val previewHeight = 500
@Test
fun headline_images() = runTest {
assertThat(shareouselViewModel.headline.first()).isEqualTo("FILES: 1")
previewSelectionsRepository.selections.value =
listOf(
- PreviewModel(
- uri = Uri.fromParts("scheme", "ssp", "fragment"),
- mimeType = "image/png",
- order = 0,
- ),
- PreviewModel(
- uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
- mimeType = "image/jpeg",
- order = 1,
+ PreviewModel(
+ uri = Uri.fromParts("scheme", "ssp", "fragment"),
+ mimeType = "image/png",
+ order = 0,
+ ),
+ PreviewModel(
+ uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
+ mimeType = "image/jpeg",
+ order = 1,
+ )
)
- ).associateBy { it.uri }
+ .associateBy { it.uri }
runCurrent()
assertThat(shareouselViewModel.headline.first()).isEqualTo("IMAGES: 2")
}
@@ -101,17 +103,18 @@ class ShareouselViewModelTest {
fun headline_videos() = runTest {
previewSelectionsRepository.selections.value =
listOf(
- PreviewModel(
- uri = Uri.fromParts("scheme", "ssp", "fragment"),
- mimeType = "video/mpeg",
- order = 0,
- ),
- PreviewModel(
- uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
- mimeType = "video/mpeg",
- order = 1,
+ PreviewModel(
+ uri = Uri.fromParts("scheme", "ssp", "fragment"),
+ mimeType = "video/mpeg",
+ order = 0,
+ ),
+ PreviewModel(
+ uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
+ mimeType = "video/mpeg",
+ order = 1,
+ )
)
- ).associateBy { it.uri }
+ .associateBy { it.uri }
runCurrent()
assertThat(shareouselViewModel.headline.first()).isEqualTo("VIDEOS: 2")
}
@@ -120,17 +123,18 @@ class ShareouselViewModelTest {
fun headline_mixed() = runTest {
previewSelectionsRepository.selections.value =
listOf(
- PreviewModel(
- uri = Uri.fromParts("scheme", "ssp", "fragment"),
- mimeType = "image/jpeg",
- order = 0,
- ),
- PreviewModel(
- uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
- mimeType = "video/mpeg",
- order = 1,
+ PreviewModel(
+ uri = Uri.fromParts("scheme", "ssp", "fragment"),
+ mimeType = "image/jpeg",
+ order = 0,
+ ),
+ PreviewModel(
+ uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
+ mimeType = "video/mpeg",
+ order = 1,
+ )
)
- ).associateBy { it.uri }
+ .associateBy { it.uri }
runCurrent()
assertThat(shareouselViewModel.headline.first()).isEqualTo("FILES: 2")
}
@@ -194,6 +198,7 @@ class ShareouselViewModelTest {
mimeType = "video/mpeg",
order = 0,
),
+ previewHeight,
/* index = */ 1,
viewModelScope,
)
@@ -245,6 +250,7 @@ class ShareouselViewModelTest {
mimeType = "video/mpeg",
order = 1,
),
+ previewHeight,
/* index = */ 1,
viewModelScope,
)
@@ -308,10 +314,11 @@ class ShareouselViewModelTest {
this.targetIntentModifier = targetIntentModifier
previewSelectionsRepository.selections.value =
PreviewModel(
- uri = Uri.fromParts("scheme", "ssp", "fragment"),
- mimeType = null,
- order = 0,
- ).let { mapOf(it.uri to it) }
+ uri = Uri.fromParts("scheme", "ssp", "fragment"),
+ mimeType = null,
+ order = 0,
+ )
+ .let { mapOf(it.uri to it) }
payloadToggleImageLoader =
FakeImageLoader(
initialBitmaps =
@@ -340,6 +347,8 @@ class ShareouselViewModelTest {
override fun getVideosHeadline(count: Int): String = "VIDEOS: $count"
override fun getFilesHeadline(count: Int): String = "FILES: $count"
+
+ override fun getNotItemsSelectedHeadline() = "Select items to share"
}
// instantiate the view model, and then runCurrent() so that it is fully hydrated before
// starting the test
diff --git a/tests/unit/src/com/android/intentresolver/ext/CreationExtrasExtTest.kt b/tests/unit/src/com/android/intentresolver/ext/CreationExtrasExtTest.kt
index c09047a1..dbaee3d0 100644
--- a/tests/unit/src/com/android/intentresolver/ext/CreationExtrasExtTest.kt
+++ b/tests/unit/src/com/android/intentresolver/ext/CreationExtrasExtTest.kt
@@ -51,4 +51,19 @@ class CreationExtrasExtTest {
assertThat(defaultArgs).parcelable<Point>("POINT1").marshallsEquallyTo(Point(1, 1))
assertThat(defaultArgs).parcelable<Point>("POINT2").marshallsEquallyTo(Point(2, 2))
}
+
+ @Test
+ fun replaceDefaultArgs_replacesExisting() {
+ val creationExtras: CreationExtras =
+ MutableCreationExtras().apply {
+ set(DEFAULT_ARGS_KEY, bundleOf("POINT1" to Point(1, 1)))
+ }
+
+ val updated = creationExtras.replaceDefaultArgs("POINT2" to Point(2, 2))
+
+ val defaultArgs = updated[DEFAULT_ARGS_KEY]
+ assertThat(defaultArgs).doesNotContainKey("POINT1")
+ assertThat(defaultArgs).containsKey("POINT2")
+ assertThat(defaultArgs).parcelable<Point>("POINT2").marshallsEquallyTo(Point(2, 2))
+ }
}
diff --git a/tests/unit/src/com/android/intentresolver/logging/EventLogImplTest.java b/tests/unit/src/com/android/intentresolver/logging/EventLogImplTest.java
index feb277ea..528c4613 100644
--- a/tests/unit/src/com/android/intentresolver/logging/EventLogImplTest.java
+++ b/tests/unit/src/com/android/intentresolver/logging/EventLogImplTest.java
@@ -152,6 +152,45 @@ public final class EventLogImplTest {
}
@Test
+ public void shareStartedWithShareouselAndEnabledReportingFlag_imagePreviewTypeReported() {
+ final String packageName = "com.test.foo";
+ final String mimeType = "text/plain";
+ final int appProvidedDirectTargets = 123;
+ final int appProvidedAppTargets = 456;
+ final boolean workProfile = true;
+ final int previewType = ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION;
+ final String intentAction = Intent.ACTION_SENDTO;
+ final int numCustomActions = 3;
+ final boolean modifyShareProvided = true;
+
+ mChooserLogger.logShareStarted(
+ packageName,
+ mimeType,
+ appProvidedDirectTargets,
+ appProvidedAppTargets,
+ workProfile,
+ previewType,
+ intentAction,
+ numCustomActions,
+ modifyShareProvided);
+
+ verify(mFrameworkLog).write(
+ eq(FrameworkStatsLog.SHARESHEET_STARTED),
+ eq(SharesheetStartedEvent.SHARE_STARTED.getId()),
+ eq(packageName),
+ /* instanceId=*/ gt(0),
+ eq(mimeType),
+ eq(appProvidedDirectTargets),
+ eq(appProvidedAppTargets),
+ eq(workProfile),
+ eq(FrameworkStatsLog
+ .SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_TOGGLEABLE_MEDIA),
+ eq(FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SENDTO),
+ /* custom actions provided */ eq(numCustomActions),
+ /* reselection action provided */ eq(modifyShareProvided));
+ }
+
+ @Test
public void testLogShareTargetSelected() {
final int targetType = EventLogImpl.SELECTION_TYPE_SERVICE;
final String packageName = "com.test.foo";
diff --git a/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt b/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt
index fbdc062b..d11cb460 100644
--- a/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt
+++ b/tests/unit/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt
@@ -26,7 +26,12 @@ import android.content.pm.PackageManager.ApplicationInfoFlags
import android.content.pm.ShortcutManager
import android.os.UserHandle
import android.os.UserManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.filters.SmallTest
+import com.android.intentresolver.Flags.FLAG_FIX_SHORTCUTS_FLASHING
+import com.android.intentresolver.Flags.FLAG_FIX_SHORTCUT_LOADER_JOB_LEAK
import com.android.intentresolver.chooser.DisplayResolveInfo
import com.android.intentresolver.createAppTarget
import com.android.intentresolver.createShareShortcutInfo
@@ -42,6 +47,7 @@ import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
+import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
@@ -56,6 +62,8 @@ import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class ShortcutLoaderTest {
+ @get:Rule val flagRule = SetFlagsRule()
+
private val appInfo =
ApplicationInfo().apply {
enabled = true
@@ -317,6 +325,143 @@ class ShortcutLoaderTest {
}
@Test
+ @DisableFlags(FLAG_FIX_SHORTCUTS_FLASHING)
+ fun test_appPredictorNotResponding_noCallbackFromShortcutLoader() {
+ scope.runTest {
+ val shortcutManagerResult =
+ listOf(
+ ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
+ // mismatching shortcut
+ createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
+ )
+ val shortcutManager =
+ mock<ShortcutManager> {
+ on { getShareTargets(intentFilter) } doReturn shortcutManagerResult
+ }
+ whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
+ val testSubject =
+ ShortcutLoader(
+ context,
+ backgroundScope,
+ appPredictor,
+ UserHandle.of(0),
+ true,
+ intentFilter,
+ dispatcher,
+ callback
+ )
+
+ testSubject.updateAppTargets(appTargets)
+
+ verify(appPredictor, times(1)).requestPredictionUpdate()
+
+ scheduler.advanceTimeBy(ShortcutLoader.APP_PREDICTOR_RESPONSE_TIMEOUT_MS * 2)
+ verify(callback, never()).accept(any())
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_FIX_SHORTCUTS_FLASHING)
+ fun test_appPredictorNotResponding_timeoutAndFallbackToShortcutManager() {
+ scope.runTest {
+ val testSubject =
+ ShortcutLoader(
+ context,
+ backgroundScope,
+ appPredictor,
+ UserHandle.of(0),
+ true,
+ intentFilter,
+ dispatcher,
+ callback
+ )
+
+ testSubject.updateAppTargets(appTargets)
+
+ val matchingAppTarget = createAppTarget(matchingShortcutInfo)
+ val shortcuts =
+ listOf(
+ matchingAppTarget,
+ // an AppTarget that does not belong to any resolved application; should be
+ // ignored
+ createAppTarget(
+ createShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
+ )
+ )
+ val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>()
+ verify(appPredictor, atLeastOnce())
+ .registerPredictionUpdates(any(), appPredictorCallbackCaptor.capture())
+ appPredictorCallbackCaptor.firstValue.onTargetsAvailable(shortcuts)
+
+ scheduler.advanceTimeBy(ShortcutLoader.APP_PREDICTOR_RESPONSE_TIMEOUT_MS * 2)
+ verify(callback, times(1)).accept(any())
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_FIX_SHORTCUTS_FLASHING)
+ fun test_appPredictorResponding_appPredictorTimeoutJobIsCancelled() {
+ scope.runTest {
+ val shortcutManagerResult =
+ listOf(
+ ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
+ // mismatching shortcut
+ createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
+ )
+ val shortcutManager =
+ mock<ShortcutManager> {
+ on { getShareTargets(intentFilter) } doReturn shortcutManagerResult
+ }
+ whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
+ val testSubject =
+ ShortcutLoader(
+ context,
+ backgroundScope,
+ appPredictor,
+ UserHandle.of(0),
+ true,
+ intentFilter,
+ dispatcher,
+ callback
+ )
+
+ testSubject.updateAppTargets(appTargets)
+
+ verify(appPredictor, times(1)).requestPredictionUpdate()
+
+ scheduler.advanceTimeBy(ShortcutLoader.APP_PREDICTOR_RESPONSE_TIMEOUT_MS / 2)
+ verify(callback, never()).accept(any())
+
+ val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
+ scheduler.advanceTimeBy(ShortcutLoader.APP_PREDICTOR_RESPONSE_TIMEOUT_MS)
+ verify(callback, times(1)).accept(resultCaptor.capture())
+ val result = resultCaptor.firstValue
+ assertWithMessage("An ShortcutManager result is expected")
+ .that(result.isFromAppPredictor)
+ .isFalse()
+ assertWithMessage("Wrong input app targets in the result")
+ .that(appTargets)
+ .asList()
+ .containsExactlyElementsIn(result.appTargets)
+ .inOrder()
+ assertWithMessage("Wrong shortcut count").that(result.shortcutsByApp).hasLength(1)
+ assertWithMessage("Wrong app target")
+ .that(appTarget)
+ .isEqualTo(result.shortcutsByApp[0].appTarget)
+ for (shortcut in result.shortcutsByApp[0].shortcuts) {
+ assertWithMessage(
+ "AppTargets are not expected the cache of a ShortcutManager result"
+ )
+ .that(result.directShareAppTargetCache)
+ .isEmpty()
+ assertWithMessage("Wrong ShortcutInfo in the cache")
+ .that(matchingShortcutInfo)
+ .isEqualTo(result.directShareShortcutInfoCache[shortcut])
+ }
+ }
+ }
+
+ @Test
fun test_ShortcutLoader_shortcutsRequestedIndependentlyFromAppTargets() =
scope.runTest {
ShortcutLoader(
@@ -465,6 +610,30 @@ class ShortcutLoaderTest {
testAlwaysCallSystemForMainProfile(isQuietModeEnabled = true)
}
+ @Test
+ @EnableFlags(FLAG_FIX_SHORTCUT_LOADER_JOB_LEAK)
+ fun test_ShortcutLoaderDestroyed_appPredictorCallbackUnregisteredAndWatchdogCancelled() {
+ scope.runTest {
+ val testSubject =
+ ShortcutLoader(
+ context,
+ backgroundScope,
+ appPredictor,
+ UserHandle.of(0),
+ true,
+ intentFilter,
+ dispatcher,
+ callback
+ )
+
+ testSubject.updateAppTargets(appTargets)
+ testSubject.destroy()
+
+ verify(appPredictor, times(1)).registerPredictionUpdates(any(), any())
+ verify(appPredictor, times(1)).unregisterPredictionUpdates(any())
+ }
+ }
+
private fun testDisabledWorkProfileDoNotCallSystem(
isUserRunning: Boolean = true,
isUserUnlocked: Boolean = true,
diff --git a/tests/unit/src/com/android/intentresolver/ui/ShareResultSenderImplTest.kt b/tests/unit/src/com/android/intentresolver/ui/ShareResultSenderImplTest.kt
index c254a856..d8b1b175 100644
--- a/tests/unit/src/com/android/intentresolver/ui/ShareResultSenderImplTest.kt
+++ b/tests/unit/src/com/android/intentresolver/ui/ShareResultSenderImplTest.kt
@@ -22,9 +22,7 @@ import android.content.ComponentName
import android.content.Intent
import android.os.Process
import android.service.chooser.ChooserResult
-import android.service.chooser.Flags
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.intentresolver.inject.FakeChooserServiceFlags
import com.android.intentresolver.ui.model.ShareAction
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
@@ -46,8 +44,6 @@ class ShareResultSenderImplTest {
@get:Rule val compatChangeRule: TestRule = PlatformCompatChangeRule()
- val flags = FakeChooserServiceFlags()
-
@OptIn(ExperimentalCoroutinesApi::class)
@EnableCompatChanges(ChooserResult.SEND_CHOOSER_RESULT)
@Test
@@ -56,11 +52,8 @@ class ShareResultSenderImplTest {
val deferred = CompletableDeferred<Intent>()
val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
- flags.setFlag(Flags.FLAG_ENABLE_CHOOSER_RESULT, true)
-
val resultSender =
ShareResultSenderImpl(
- flags = flags,
scope = this,
backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
callerUid = Process.myUid(),
@@ -68,7 +61,7 @@ class ShareResultSenderImplTest {
intentDispatcher = intentDispatcher
)
- resultSender.onComponentSelected(ComponentName("example.com", "Foo"), true)
+ resultSender.onComponentSelected(ComponentName("example.com", "Foo"), true, false)
runCurrent()
val intentReceived = deferred.await()
@@ -83,6 +76,40 @@ class ShareResultSenderImplTest {
assertThat(chooserResult?.isShortcut).isTrue()
}
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @EnableCompatChanges(ChooserResult.SEND_CHOOSER_RESULT)
+ @Test
+ fun onComponentSelected_crossProfile_chooserResultEnabled() = runTest {
+ val pi = PendingIntent.getBroadcast(context, 0, Intent(), PendingIntent.FLAG_IMMUTABLE)
+ val deferred = CompletableDeferred<Intent>()
+ val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
+
+ val resultSender =
+ ShareResultSenderImpl(
+ scope = this,
+ backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
+ callerUid = Process.myUid(),
+ resultSender = pi.intentSender,
+ intentDispatcher = intentDispatcher
+ )
+
+ // Invoke as in the previous test, but this time say that the selection was cross-profile.
+ resultSender.onComponentSelected(ComponentName("example.com", "Foo"), true, true)
+ runCurrent()
+
+ val intentReceived = deferred.await()
+ val chooserResult =
+ intentReceived.getParcelableExtra(
+ Intent.EXTRA_CHOOSER_RESULT,
+ ChooserResult::class.java
+ )
+ assertThat(chooserResult).isNotNull()
+ assertThat(chooserResult?.type).isEqualTo(ChooserResult.CHOOSER_RESULT_UNKNOWN)
+ assertThat(chooserResult?.selectedComponent).isNull()
+ assertThat(chooserResult?.isShortcut).isTrue()
+ assertThat(intentReceived.hasExtra(Intent.EXTRA_CHOSEN_COMPONENT)).isFalse()
+ }
+
@DisableCompatChanges(ChooserResult.SEND_CHOOSER_RESULT)
@Test
fun onComponentSelected_chooserResultDisabled() = runTest {
@@ -90,11 +117,8 @@ class ShareResultSenderImplTest {
val deferred = CompletableDeferred<Intent>()
val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
- flags.setFlag(Flags.FLAG_ENABLE_CHOOSER_RESULT, true)
-
val resultSender =
ShareResultSenderImpl(
- flags = flags,
scope = this,
backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
callerUid = Process.myUid(),
@@ -102,7 +126,7 @@ class ShareResultSenderImplTest {
intentDispatcher = intentDispatcher
)
- resultSender.onComponentSelected(ComponentName("example.com", "Foo"), true)
+ resultSender.onComponentSelected(ComponentName("example.com", "Foo"), true, false)
runCurrent()
val intentReceived = deferred.await()
@@ -121,6 +145,30 @@ class ShareResultSenderImplTest {
.isFalse()
}
+ @DisableCompatChanges(ChooserResult.SEND_CHOOSER_RESULT)
+ @Test
+ fun onComponentSelected_crossProfile_chooserResultDisabled() = runTest {
+ val pi = PendingIntent.getBroadcast(context, 0, Intent(), PendingIntent.FLAG_IMMUTABLE)
+ val deferred = CompletableDeferred<Intent>()
+ val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
+
+ val resultSender =
+ ShareResultSenderImpl(
+ scope = this,
+ backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
+ callerUid = Process.myUid(),
+ resultSender = pi.intentSender,
+ intentDispatcher = intentDispatcher
+ )
+
+ // Invoke as in the previous test, but this time say that the selection was cross-profile.
+ resultSender.onComponentSelected(ComponentName("example.com", "Foo"), true, true)
+ runCurrent()
+
+ // In the pre-ChooserResult API, no callback intent is sent for cross-profile selections.
+ assertWithMessage("deferred result isComplete").that(deferred.isCompleted).isFalse()
+ }
+
@EnableCompatChanges(ChooserResult.SEND_CHOOSER_RESULT)
@Test
fun onActionSelected_chooserResultEnabled() = runTest {
@@ -128,11 +176,8 @@ class ShareResultSenderImplTest {
val deferred = CompletableDeferred<Intent>()
val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
- flags.setFlag(Flags.FLAG_ENABLE_CHOOSER_RESULT, true)
-
val resultSender =
ShareResultSenderImpl(
- flags = flags,
scope = this,
backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
callerUid = Process.myUid(),
@@ -169,11 +214,8 @@ class ShareResultSenderImplTest {
val deferred = CompletableDeferred<Intent>()
val intentDispatcher = IntentSenderDispatcher { _, intent -> deferred.complete(intent) }
- flags.setFlag(Flags.FLAG_ENABLE_CHOOSER_RESULT, true)
-
val resultSender =
ShareResultSenderImpl(
- flags = flags,
scope = this,
backgroundDispatcher = UnconfinedTestDispatcher(testScheduler),
callerUid = Process.myUid(),
diff --git a/tests/unit/src/com/android/intentresolver/ui/model/ActivityModelTest.kt b/tests/unit/src/com/android/intentresolver/ui/model/ActivityModelTest.kt
index 737f02fe..5f86159c 100644
--- a/tests/unit/src/com/android/intentresolver/ui/model/ActivityModelTest.kt
+++ b/tests/unit/src/com/android/intentresolver/ui/model/ActivityModelTest.kt
@@ -21,6 +21,7 @@ import android.content.Intent.ACTION_CHOOSER
import android.content.Intent.EXTRA_TEXT
import android.net.Uri
import com.android.intentresolver.ext.toParcelAndBack
+import com.android.intentresolver.shared.model.ActivityModel
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Test
@@ -54,7 +55,7 @@ class ActivityModelTest {
intent = Intent(),
launchedFromUid = 1000,
launchedFromPackage = "other.example.com",
- referrer = Uri.parse("android-app://app.example.com")
+ referrer = Uri.parse("android-app://app.example.com"),
)
assertThat(launch1.referrerPackage).isEqualTo("app.example.com")
@@ -67,7 +68,7 @@ class ActivityModelTest {
intent = Intent(),
launchedFromUid = 1000,
launchedFromPackage = "example.com",
- referrer = Uri.parse("http://some.other.value")
+ referrer = Uri.parse("http://some.other.value"),
)
assertThat(launch.referrerPackage).isNull()
@@ -80,7 +81,7 @@ class ActivityModelTest {
intent = Intent(),
launchedFromUid = 1000,
launchedFromPackage = "example.com",
- referrer = null
+ referrer = null,
)
assertThat(launch.referrerPackage).isNull()
diff --git a/tests/unit/src/com/android/intentresolver/ui/viewmodel/ChooserRequestTest.kt b/tests/unit/src/com/android/intentresolver/ui/viewmodel/ChooserRequestTest.kt
index 56c019fd..7bd4edee 100644
--- a/tests/unit/src/com/android/intentresolver/ui/viewmodel/ChooserRequestTest.kt
+++ b/tests/unit/src/com/android/intentresolver/ui/viewmodel/ChooserRequestTest.kt
@@ -25,6 +25,8 @@ import android.content.Intent.EXTRA_CHOOSER_ADDITIONAL_CONTENT_URI
import android.content.Intent.EXTRA_CHOOSER_FOCUSED_ITEM_POSITION
import android.content.Intent.EXTRA_INTENT
import android.content.Intent.EXTRA_REFERRER
+import android.content.Intent.EXTRA_TEXT
+import android.content.Intent.EXTRA_TITLE
import android.net.Uri
import android.service.chooser.Flags
import androidx.core.net.toUri
@@ -32,7 +34,7 @@ import androidx.core.os.bundleOf
import com.android.intentresolver.ContentTypeHint
import com.android.intentresolver.data.model.ChooserRequest
import com.android.intentresolver.inject.FakeChooserServiceFlags
-import com.android.intentresolver.ui.model.ActivityModel
+import com.android.intentresolver.shared.model.ActivityModel
import com.android.intentresolver.validation.Importance
import com.android.intentresolver.validation.Invalid
import com.android.intentresolver.validation.NoValue
@@ -43,7 +45,7 @@ import org.junit.Test
private fun createActivityModel(
targetIntent: Intent?,
referrer: Uri? = null,
- additionalIntents: List<Intent>? = null
+ additionalIntents: List<Intent>? = null,
) =
ActivityModel(
Intent(ACTION_CHOOSER).apply {
@@ -52,17 +54,13 @@ private fun createActivityModel(
},
launchedFromUid = 10000,
launchedFromPackage = "com.android.example",
- referrer = referrer ?: "android-app://com.android.example".toUri()
+ referrer = referrer ?: "android-app://com.android.example".toUri(),
)
class ChooserRequestTest {
private val fakeChooserServiceFlags =
- FakeChooserServiceFlags().apply {
- setFlag(Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING, false)
- setFlag(Flags.FLAG_CHOOSER_ALBUM_TEXT, false)
- setFlag(Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA, false)
- }
+ FakeChooserServiceFlags().apply { setFlag(Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING, false) }
@Test
fun missingIntent() {
@@ -244,11 +242,10 @@ class ChooserRequestTest {
@Test
fun testAlbumType() {
- fakeChooserServiceFlags.setFlag(Flags.FLAG_CHOOSER_ALBUM_TEXT, true)
val model = createActivityModel(Intent(ACTION_SEND))
model.intent.putExtra(
Intent.EXTRA_CHOOSER_CONTENT_TYPE_HINT,
- Intent.CHOOSER_CONTENT_TYPE_ALBUM
+ Intent.CHOOSER_CONTENT_TYPE_ALBUM,
)
val result = readChooserRequest(model, fakeChooserServiceFlags)
@@ -261,8 +258,8 @@ class ChooserRequestTest {
}
@Test
- fun metadataText_whenFlagFalse_isNull() {
- fakeChooserServiceFlags.setFlag(Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA, false)
+ fun metadataText_isPassedText() {
+ // Arrange
val metadataText: CharSequence = "Test metadata text"
val model =
createActivityModel(targetIntent = Intent()).apply {
@@ -274,24 +271,26 @@ class ChooserRequestTest {
assertThat(result).isInstanceOf(Valid::class.java)
result as Valid<ChooserRequest>
- assertThat(result.value.metadataText).isNull()
+ assertThat(result.value.metadataText).isEqualTo(metadataText)
}
@Test
- fun metadataText_whenFlagTrue_isPassedText() {
- // Arrange
- fakeChooserServiceFlags.setFlag(Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA, true)
- val metadataText: CharSequence = "Test metadata text"
- val model =
- createActivityModel(targetIntent = Intent()).apply {
- intent.putExtra(Intent.EXTRA_METADATA_TEXT, metadataText)
+ fun textSharedTextAndTitle() {
+ val text: CharSequence = "Shared text"
+ val title: CharSequence = "Title"
+ val targetIntent =
+ Intent().apply {
+ putExtra(EXTRA_TITLE, title)
+ putExtra(EXTRA_TEXT, text)
}
+ val model = createActivityModel(targetIntent)
val result = readChooserRequest(model, fakeChooserServiceFlags)
assertThat(result).isInstanceOf(Valid::class.java)
- result as Valid<ChooserRequest>
-
- assertThat(result.value.metadataText).isEqualTo(metadataText)
+ (result as Valid<ChooserRequest>).value.let { request ->
+ assertThat(request.sharedText).isEqualTo(text)
+ assertThat(request.sharedTextTitle).isEqualTo(title)
+ }
}
}
diff --git a/tests/unit/src/com/android/intentresolver/ui/viewmodel/ResolverRequestTest.kt b/tests/unit/src/com/android/intentresolver/ui/viewmodel/ResolverRequestTest.kt
index bd80235d..70512021 100644
--- a/tests/unit/src/com/android/intentresolver/ui/viewmodel/ResolverRequestTest.kt
+++ b/tests/unit/src/com/android/intentresolver/ui/viewmodel/ResolverRequestTest.kt
@@ -22,8 +22,8 @@ import android.os.UserHandle
import androidx.core.net.toUri
import androidx.core.os.bundleOf
import com.android.intentresolver.ResolverActivity.PROFILE_WORK
+import com.android.intentresolver.shared.model.ActivityModel
import com.android.intentresolver.shared.model.Profile.Type.WORK
-import com.android.intentresolver.ui.model.ActivityModel
import com.android.intentresolver.ui.model.ResolverRequest
import com.android.intentresolver.validation.Invalid
import com.android.intentresolver.validation.UncaughtException
@@ -34,15 +34,12 @@ import org.junit.Test
private val targetUri = Uri.parse("content://example.com/123")
-private fun createActivityModel(
- targetIntent: Intent,
- referrer: Uri? = null,
-) =
+private fun createActivityModel(targetIntent: Intent, referrer: Uri? = null) =
ActivityModel(
intent = targetIntent,
launchedFromUid = 10000,
launchedFromPackage = "com.android.example",
- referrer = referrer ?: "android-app://com.android.example".toUri()
+ referrer = referrer ?: "android-app://com.android.example".toUri(),
)
class ResolverRequestTest {
diff --git a/tests/unit/src/com/android/intentresolver/widget/BatchPreviewLoaderTest.kt b/tests/unit/src/com/android/intentresolver/widget/BatchPreviewLoaderTest.kt
index 4f4223c0..b1e8593d 100644
--- a/tests/unit/src/com/android/intentresolver/widget/BatchPreviewLoaderTest.kt
+++ b/tests/unit/src/com/android/intentresolver/widget/BatchPreviewLoaderTest.kt
@@ -18,6 +18,7 @@ package com.android.intentresolver.widget
import android.graphics.Bitmap
import android.net.Uri
+import android.util.Size
import com.android.intentresolver.captureMany
import com.android.intentresolver.mock
import com.android.intentresolver.widget.ScrollableImagePreviewView.BatchPreviewLoader
@@ -49,6 +50,7 @@ class BatchPreviewLoaderTest {
private val testScope = CoroutineScope(dispatcher)
private val onCompletion = mock<() -> Unit>()
private val onUpdate = mock<(List<Preview>) -> Unit>()
+ private val previewSize = Size(500, 500)
@Before
fun setup() {
@@ -71,6 +73,7 @@ class BatchPreviewLoaderTest {
BatchPreviewLoader(
imageLoader,
previews(uriOne, uriTwo),
+ previewSize,
totalItemCount = 2,
onUpdate,
onCompletion
@@ -94,6 +97,7 @@ class BatchPreviewLoaderTest {
BatchPreviewLoader(
imageLoader,
previews(uriOne, uriTwo, uriThree),
+ previewSize,
totalItemCount = 3,
onUpdate,
onCompletion
@@ -122,7 +126,14 @@ class BatchPreviewLoaderTest {
}
imageLoader.setUriLoadingOrder(*loadingOrder)
val testSubject =
- BatchPreviewLoader(imageLoader, previews(*uris), uris.size, onUpdate, onCompletion)
+ BatchPreviewLoader(
+ imageLoader,
+ previews(*uris),
+ previewSize,
+ uris.size,
+ onUpdate,
+ onCompletion
+ )
testSubject.loadAspectRatios(200) { _, _, _ -> 100 }
dispatcher.scheduler.advanceUntilIdle()
@@ -151,7 +162,14 @@ class BatchPreviewLoaderTest {
val expectedUris = Array(uris.size / 2) { createUri(it * 2 + 1) }
imageLoader.setUriLoadingOrder(*loadingOrder)
val testSubject =
- BatchPreviewLoader(imageLoader, previews(*uris), uris.size, onUpdate, onCompletion)
+ BatchPreviewLoader(
+ imageLoader,
+ previews(*uris),
+ previewSize,
+ uris.size,
+ onUpdate,
+ onCompletion
+ )
testSubject.loadAspectRatios(200) { _, _, _ -> 100 }
dispatcher.scheduler.advanceUntilIdle()
@@ -166,7 +184,9 @@ class BatchPreviewLoaderTest {
private fun createUri(idx: Int): Uri = Uri.parse("content://org.pkg.app/image-$idx.png")
private fun fail(uri: Uri) = uri to false
+
private fun succeed(uri: Uri) = uri to true
+
private fun previews(vararg uris: Uri) =
uris
.fold(ArrayList<Preview>(uris.size)) { acc, uri ->
@@ -175,7 +195,7 @@ class BatchPreviewLoaderTest {
.asFlow()
}
-private class TestImageLoader(scope: CoroutineScope) : suspend (Uri, Boolean) -> Bitmap? {
+private class TestImageLoader(scope: CoroutineScope) : suspend (Uri, Size, Boolean) -> Bitmap? {
private val loadingOrder = ArrayDeque<Pair<Uri, Boolean>>()
private val pendingRequests = LinkedHashMap<Uri, CompletableDeferred<Bitmap?>>()
private val flow = MutableSharedFlow<Unit>(replay = 1)
@@ -203,7 +223,7 @@ private class TestImageLoader(scope: CoroutineScope) : suspend (Uri, Boolean) ->
loadingOrder.addAll(uris)
}
- override suspend fun invoke(uri: Uri, cache: Boolean): Bitmap? {
+ override suspend fun invoke(uri: Uri, size: Size, cache: Boolean): Bitmap? {
val deferred = pendingRequests.getOrPut(uri) { CompletableDeferred() }
flow.tryEmit(Unit)
return deferred.await()