summaryrefslogtreecommitdiff
path: root/java/tests
diff options
context:
space:
mode:
author Xin Li <delphij@google.com> 2023-04-18 16:34:38 -0700
committer Xin Li <delphij@google.com> 2023-04-18 16:34:38 -0700
commit4042e26988acfecda45dbcc4d01ac1be7813b42e (patch)
tree970a428d34bfaad9fd8e70ad12619586033d18a7 /java/tests
parentd36ad81bb94eb3178d53a227c98692e559465ea2 (diff)
parent4bc99bb4b351fe2304b3c7e147248a28c507ae57 (diff)
Merge Android 13 QPR3 tm-qpr-dev-plus-aosp-without-vendor@9936994
Bug: 275386652 Merged-In: I392de610b3d3e044e23c83d29fd11061fbc7192d Change-Id: Ib64b6b991713c518faaab01935cad9e8a57e0d98
Diffstat (limited to 'java/tests')
-rw-r--r--java/tests/Android.bp5
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt154
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserActivityLoggerTest.java28
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java29
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserIntegratedDeviceComponentsTest.kt71
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt61
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java54
-rw-r--r--java/tests/src/com/android/intentresolver/EnterTransitionAnimationDelegateTest.kt112
-rw-r--r--java/tests/src/com/android/intentresolver/FeatureFlagRule.kt56
-rw-r--r--java/tests/src/com/android/intentresolver/ImagePreviewImageLoaderTest.kt101
-rw-r--r--java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt3
-rw-r--r--java/tests/src/com/android/intentresolver/RequireFeatureFlags.kt23
-rw-r--r--java/tests/src/com/android/intentresolver/ResolverActivityTest.java61
-rw-r--r--java/tests/src/com/android/intentresolver/ResolverDataProvider.java52
-rw-r--r--java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java28
-rw-r--r--java/tests/src/com/android/intentresolver/TestFeatureFlagRepository.kt31
-rw-r--r--java/tests/src/com/android/intentresolver/TestLifecycleOwner.kt33
-rw-r--r--java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt38
-rw-r--r--java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java886
-rw-r--r--java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java21
-rw-r--r--java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt497
-rw-r--r--java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt212
-rw-r--r--java/tests/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt203
-rw-r--r--java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java12
-rw-r--r--java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt58
25 files changed, 2279 insertions, 550 deletions
diff --git a/java/tests/Android.bp b/java/tests/Android.bp
index 2913d128..4e835ec8 100644
--- a/java/tests/Android.bp
+++ b/java/tests/Android.bp
@@ -21,11 +21,16 @@ android_test {
"IntentResolver-core",
"androidx.test.rules",
"androidx.test.ext.junit",
+ "androidx.test.espresso.contrib",
"mockito-target-minus-junit4",
"androidx.test.espresso.core",
+ "androidx.lifecycle_lifecycle-common-java8",
+ "androidx.lifecycle_lifecycle-extensions",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
"truth-prebuilt",
"testables",
"testng",
+ "kotlinx_coroutines_test",
],
test_suites: ["general-tests"],
sdk_version: "core_platform",
diff --git a/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt b/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt
new file mode 100644
index 00000000..af134fcd
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.app.Activity
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.res.Resources
+import android.graphics.drawable.Icon
+import android.service.chooser.ChooserAction
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.intentresolver.flags.FeatureFlagRepository
+import com.android.intentresolver.flags.Flags
+import com.google.common.collect.ImmutableList
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import java.util.concurrent.Callable
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import java.util.function.Consumer
+
+@RunWith(AndroidJUnit4::class)
+class ChooserActionFactoryTest {
+ private val context = InstrumentationRegistry.getInstrumentation().getContext()
+
+ private val logger = mock<ChooserActivityLogger>()
+ private val flags = mock<FeatureFlagRepository>()
+ private val actionLabel = "Action label"
+ private val testAction = "com.android.intentresolver.testaction"
+ private val countdown = CountDownLatch(1)
+ private val testReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ // Just doing at most a single countdown per test.
+ countdown.countDown()
+ }
+ }
+ private object resultConsumer : Consumer<Int> {
+ var latestReturn = Integer.MIN_VALUE
+
+ override fun accept(resultCode: Int) {
+ latestReturn = resultCode
+ }
+
+ }
+
+ @Before
+ fun setup() {
+ whenever(flags.isEnabled(Flags.SHARESHEET_RESELECTION_ACTION)).thenReturn(true)
+ context.registerReceiver(testReceiver, IntentFilter(testAction))
+ }
+
+ @After
+ fun teardown() {
+ context.unregisterReceiver(testReceiver)
+ }
+
+ @Test
+ fun testCreateCustomActions() {
+ val factory = createFactory()
+
+ val customActions = factory.createCustomActions()
+
+ assertThat(customActions.size).isEqualTo(1)
+ assertThat(customActions[0].label).isEqualTo(actionLabel)
+
+ // click it
+ customActions[0].onClicked.run()
+
+ Mockito.verify(logger).logCustomActionSelected(eq(0))
+ assertEquals(Activity.RESULT_OK, resultConsumer.latestReturn)
+ // Verify the pendingintent has been called
+ countdown.await(500, TimeUnit.MILLISECONDS)
+ }
+
+ @Test
+ fun testNoModifyShareAction() {
+ val factory = createFactory(includeModifyShare = false)
+
+ assertThat(factory.modifyShareAction).isNull()
+ }
+
+ @Test
+ fun testNoModifyShareAction_flagDisabled() {
+ whenever(flags.isEnabled(Flags.SHARESHEET_RESELECTION_ACTION)).thenReturn(false)
+ val factory = createFactory(includeModifyShare = true)
+
+ assertThat(factory.modifyShareAction).isNull()
+ }
+
+ @Test
+ fun testModifyShareAction() {
+ val factory = createFactory(includeModifyShare = true)
+
+ factory.modifyShareAction!!.run()
+
+ Mockito.verify(logger).logActionSelected(
+ eq(ChooserActivityLogger.SELECTION_TYPE_MODIFY_SHARE))
+ assertEquals(Activity.RESULT_OK, resultConsumer.latestReturn)
+ // Verify the pendingintent has been called
+ countdown.await(500, TimeUnit.MILLISECONDS)
+ }
+
+ private fun createFactory(includeModifyShare: Boolean = false): ChooserActionFactory {
+ val testPendingIntent = PendingIntent.getActivity(context, 0, Intent(testAction),0)
+ val targetIntent = Intent()
+ val action = ChooserAction.Builder(
+ Icon.createWithResource("", Resources.ID_NULL),
+ actionLabel,
+ testPendingIntent
+ ).build()
+ val chooserRequest = mock<ChooserRequestParameters>()
+ whenever(chooserRequest.targetIntent).thenReturn(targetIntent)
+ whenever(chooserRequest.chooserActions).thenReturn(ImmutableList.of(action))
+
+ if (includeModifyShare) {
+ whenever(chooserRequest.modifyShareAction).thenReturn(testPendingIntent)
+ }
+
+ return ChooserActionFactory(
+ context,
+ chooserRequest,
+ flags,
+ mock<ChooserIntegratedDeviceComponents>(),
+ logger,
+ Consumer<Boolean>{},
+ Callable<View?>{null},
+ mock<ChooserActionFactory.ActionActivityStarter>(),
+ resultConsumer)
+ }
+} \ No newline at end of file
diff --git a/java/tests/src/com/android/intentresolver/ChooserActivityLoggerTest.java b/java/tests/src/com/android/intentresolver/ChooserActivityLoggerTest.java
index 705a3228..aa42c24c 100644
--- a/java/tests/src/com/android/intentresolver/ChooserActivityLoggerTest.java
+++ b/java/tests/src/com/android/intentresolver/ChooserActivityLoggerTest.java
@@ -36,6 +36,7 @@ import com.android.intentresolver.ChooserActivityLogger.FrameworkStatsLogger;
import com.android.intentresolver.ChooserActivityLogger.SharesheetStandardEvent;
import com.android.intentresolver.ChooserActivityLogger.SharesheetStartedEvent;
import com.android.intentresolver.ChooserActivityLogger.SharesheetTargetSelectedEvent;
+import com.android.intentresolver.contentpreview.ContentPreviewType;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
@@ -112,24 +113,26 @@ public final class ChooserActivityLoggerTest {
@Test
public void testLogShareStarted() {
- final int eventId = -1; // Passed-in eventId is unused. TODO: remove from method signature.
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 = ChooserContentPreviewUi.CONTENT_PREVIEW_FILE;
+ final int previewType = ContentPreviewType.CONTENT_PREVIEW_FILE;
final String intentAction = Intent.ACTION_SENDTO;
+ final int numCustomActions = 3;
+ final boolean modifyShareProvided = true;
mChooserLogger.logShareStarted(
- eventId,
packageName,
mimeType,
appProvidedDirectTargets,
appProvidedAppTargets,
workProfile,
previewType,
- intentAction);
+ intentAction,
+ numCustomActions,
+ modifyShareProvided);
verify(mFrameworkLog).write(
eq(FrameworkStatsLog.SHARESHEET_STARTED),
@@ -141,7 +144,9 @@ public final class ChooserActivityLoggerTest {
eq(appProvidedAppTargets),
eq(workProfile),
eq(FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_FILE),
- eq(FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SENDTO));
+ eq(FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SENDTO),
+ /* custom actions provided */ eq(numCustomActions),
+ /* reselection action provided */ eq(modifyShareProvided));
}
@Test
@@ -203,6 +208,17 @@ public final class ChooserActivityLoggerTest {
}
@Test
+ public void testLogCustomActionSelected() {
+ final int position = 4;
+ mChooserLogger.logCustomActionSelected(position);
+
+ verify(mFrameworkLog).write(
+ eq(FrameworkStatsLog.RANKING_SELECTED),
+ eq(SharesheetTargetSelectedEvent.SHARESHEET_CUSTOM_ACTION_SELECTED.getId()),
+ any(), anyInt(), eq(position), eq(false));
+ }
+
+ @Test
public void testLogDirectShareTargetReceived() {
final int category = MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER;
final int latency = 123;
@@ -218,7 +234,7 @@ public final class ChooserActivityLoggerTest {
@Test
public void testLogActionShareWithPreview() {
- final int previewType = ChooserContentPreviewUi.CONTENT_PREVIEW_TEXT;
+ final int previewType = ContentPreviewType.CONTENT_PREVIEW_TEXT;
mChooserLogger.logActionShareWithPreview(previewType);
diff --git a/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java b/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java
index 5df0d4a2..f0c459e5 100644
--- a/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java
+++ b/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java
@@ -29,8 +29,8 @@ import android.os.UserHandle;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
-import com.android.intentresolver.AbstractMultiProfilePagerAdapter.QuietModeManager;
import com.android.intentresolver.chooser.TargetInfo;
+import com.android.intentresolver.flags.FeatureFlagRepository;
import com.android.intentresolver.shortcuts.ShortcutLoader;
import java.util.function.Consumer;
@@ -58,8 +58,8 @@ public class ChooserActivityOverrideData {
public Function<TargetInfo, Boolean> onSafelyStartCallback;
public Function2<UserHandle, Consumer<ShortcutLoader.Result>, ShortcutLoader>
shortcutLoaderFactory = (userHandle, callback) -> null;
- public ResolverListController resolverListController;
- public ResolverListController workResolverListController;
+ public ChooserActivity.ChooserListController resolverListController;
+ public ChooserActivity.ChooserListController workResolverListController;
public Boolean isVoiceInteraction;
public boolean isImageType;
public Cursor resolverCursor;
@@ -72,10 +72,11 @@ public class ChooserActivityOverrideData {
public boolean hasCrossProfileIntents;
public boolean isQuietModeEnabled;
public Integer myUserId;
- public QuietModeManager mQuietModeManager;
+ public WorkProfileAvailabilityManager mWorkProfileAvailability;
public MyUserIdProvider mMyUserIdProvider;
public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
public PackageManager packageManager;
+ public FeatureFlagRepository featureFlagRepository;
public void reset() {
onSafelyStartCallback = null;
@@ -85,8 +86,8 @@ public class ChooserActivityOverrideData {
isImageType = false;
resolverCursor = null;
resolverForceException = false;
- resolverListController = mock(ResolverListController.class);
- workResolverListController = mock(ResolverListController.class);
+ resolverListController = mock(ChooserActivity.ChooserListController.class);
+ workResolverListController = mock(ChooserActivity.ChooserListController.class);
chooserActivityLogger = mock(ChooserActivityLogger.class);
alternateProfileSetting = 0;
resources = null;
@@ -95,23 +96,26 @@ public class ChooserActivityOverrideData {
isQuietModeEnabled = false;
myUserId = null;
packageManager = null;
- mQuietModeManager = new QuietModeManager() {
+ mWorkProfileAvailability = new WorkProfileAvailabilityManager(null, null, null) {
@Override
- public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ public boolean isQuietModeEnabled() {
return isQuietModeEnabled;
}
@Override
- public void requestQuietModeEnabled(boolean enabled,
- UserHandle workProfileUserHandle) {
- isQuietModeEnabled = enabled;
+ public boolean isWorkProfileUserUnlocked() {
+ return true;
}
@Override
- public void markWorkProfileEnabledBroadcastReceived() {
+ public void requestQuietModeEnabled(boolean enabled) {
+ isQuietModeEnabled = enabled;
}
@Override
+ public void markWorkProfileEnabledBroadcastReceived() {}
+
+ @Override
public boolean isWaitingToEnableWorkProfile() {
return false;
}
@@ -128,6 +132,7 @@ public class ChooserActivityOverrideData {
mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class);
when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt()))
.thenAnswer(invocation -> hasCrossProfileIntents);
+ featureFlagRepository = null;
}
private ChooserActivityOverrideData() {}
diff --git a/java/tests/src/com/android/intentresolver/ChooserIntegratedDeviceComponentsTest.kt b/java/tests/src/com/android/intentresolver/ChooserIntegratedDeviceComponentsTest.kt
new file mode 100644
index 00000000..9a5dabdb
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/ChooserIntegratedDeviceComponentsTest.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.ComponentName
+import android.provider.Settings
+import android.testing.TestableContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ChooserIntegratedDeviceComponentsTest {
+ private val secureSettings = mock<SecureSettings>()
+ private val testableContext =
+ TestableContext(InstrumentationRegistry.getInstrumentation().getContext())
+
+ @Test
+ fun testEditorAndNearby() {
+ val resources = testableContext.getOrCreateTestableResources()
+
+ resources.addOverride(R.string.config_systemImageEditor, "")
+ resources.addOverride(R.string.config_defaultNearbySharingComponent, "")
+
+ var components = ChooserIntegratedDeviceComponents.get(testableContext, secureSettings)
+
+ assertThat(components.editSharingComponent).isNull()
+ assertThat(components.nearbySharingComponent).isNull()
+
+ val editor = ComponentName.unflattenFromString("com.android/com.android.Editor")
+ val nearby = ComponentName.unflattenFromString("com.android/com.android.nearby")
+
+ resources.addOverride(R.string.config_systemImageEditor, editor?.flattenToString())
+ resources.addOverride(
+ R.string.config_defaultNearbySharingComponent, nearby?.flattenToString())
+
+ components = ChooserIntegratedDeviceComponents.get(testableContext, secureSettings)
+
+ assertThat(components.editSharingComponent).isEqualTo(editor)
+ assertThat(components.nearbySharingComponent).isEqualTo(nearby)
+
+ val anotherNearby =
+ ComponentName.unflattenFromString("com.android/com.android.another_nearby")
+ whenever(
+ secureSettings.getString(
+ any(),
+ eq(Settings.Secure.NEARBY_SHARING_COMPONENT)
+ )
+ ).thenReturn(anotherNearby?.flattenToString())
+
+ components = ChooserIntegratedDeviceComponents.get(testableContext, secureSettings)
+
+ assertThat(components.nearbySharingComponent).isEqualTo(anotherNearby)
+ }
+}
diff --git a/java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt b/java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt
new file mode 100644
index 00000000..50c37c7f
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.Context
+import android.content.Intent
+import android.content.IntentSender
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.intentresolver.chooser.TargetInfo
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito
+import java.util.function.Consumer
+import org.junit.Assert.assertEquals
+
+@RunWith(AndroidJUnit4::class)
+class ChooserRefinementManagerTest {
+ @Test
+ fun testMaybeHandleSelection() {
+ val intentSender = mock<IntentSender>()
+ val refinementManager = ChooserRefinementManager(
+ mock<Context>(),
+ intentSender,
+ Consumer<TargetInfo>{},
+ Runnable{})
+
+ val intents = listOf(Intent(Intent.ACTION_VIEW), Intent(Intent.ACTION_EDIT))
+ val targetInfo = mock<TargetInfo>{
+ whenever(allSourceIntents).thenReturn(intents)
+ }
+
+ refinementManager.maybeHandleSelection(targetInfo)
+
+ val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+ Mockito.verify(intentSender).sendIntent(
+ any(), eq(0), intentCaptor.capture(), eq(null), eq(null))
+
+ val intent = intentCaptor.value
+ assertEquals(intents[0], intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java))
+
+ val alternates =
+ intent.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS, Intent::class.java)
+ assertEquals(1, alternates?.size)
+ assertEquals(intents[1], alternates?.get(0))
+ }
+}
diff --git a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java
index 97de97f5..d4ae666b 100644
--- a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java
+++ b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java
@@ -16,8 +16,6 @@
package com.android.intentresolver;
-import static org.mockito.Mockito.when;
-
import android.annotation.Nullable;
import android.app.prediction.AppPredictor;
import android.app.usage.UsageStatsManager;
@@ -30,17 +28,14 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.Cursor;
-import android.graphics.Bitmap;
import android.net.Uri;
import android.os.UserHandle;
-import android.util.Size;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
-import com.android.intentresolver.AbstractMultiProfilePagerAdapter.QuietModeManager;
import com.android.intentresolver.chooser.DisplayResolveInfo;
-import com.android.intentresolver.chooser.NotSelectableTargetInfo;
import com.android.intentresolver.chooser.TargetInfo;
+import com.android.intentresolver.flags.FeatureFlagRepository;
import com.android.intentresolver.grid.ChooserGridAdapter;
import com.android.intentresolver.shortcuts.ShortcutLoader;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -121,15 +116,13 @@ public class ChooserWrapperActivity
}
@Override
- protected ComponentName getNearbySharingComponent() {
- // an arbitrary pre-installed activity that handles this type of intent
- return ComponentName.unflattenFromString("com.google.android.apps.messaging/"
- + "com.google.android.apps.messaging.ui.conversationlist.ShareIntentActivity");
- }
-
- @Override
- protected TargetInfo getNearbySharingTarget(Intent originalIntent) {
- return NotSelectableTargetInfo.newEmptyTargetInfo();
+ protected ChooserIntegratedDeviceComponents getIntegratedDeviceComponents() {
+ return new ChooserIntegratedDeviceComponents(
+ /* editSharingComponent=*/ null,
+ // An arbitrary pre-installed activity that handles this type of intent:
+ /* nearbySharingComponent=*/ new ComponentName(
+ "com.google.android.apps.messaging",
+ ".ui.conversationlist.ShareIntentActivity"));
}
@Override
@@ -165,15 +158,15 @@ public class ChooserWrapperActivity
}
@Override
- protected QuietModeManager createQuietModeManager() {
- if (sOverrides.mQuietModeManager != null) {
- return sOverrides.mQuietModeManager;
+ protected WorkProfileAvailabilityManager createWorkProfileAvailabilityManager() {
+ if (sOverrides.mWorkProfileAvailability != null) {
+ return sOverrides.mWorkProfileAvailability;
}
- return super.createQuietModeManager();
+ return super.createWorkProfileAvailabilityManager();
}
@Override
- public void safelyStartActivity(com.android.intentresolver.chooser.TargetInfo cti) {
+ public void safelyStartActivity(TargetInfo cti) {
if (sOverrides.onSafelyStartCallback != null
&& sOverrides.onSafelyStartCallback.apply(cti)) {
return;
@@ -182,12 +175,10 @@ public class ChooserWrapperActivity
}
@Override
- protected ResolverListController createListController(UserHandle userHandle) {
+ protected ChooserListController createListController(UserHandle userHandle) {
if (userHandle == UserHandle.SYSTEM) {
- when(sOverrides.resolverListController.getUserHandle()).thenReturn(UserHandle.SYSTEM);
return sOverrides.resolverListController;
}
- when(sOverrides.workResolverListController.getUserHandle()).thenReturn(userHandle);
return sOverrides.workResolverListController;
}
@@ -208,11 +199,10 @@ public class ChooserWrapperActivity
}
@Override
- protected Bitmap loadThumbnail(Uri uri, Size size) {
- if (sOverrides.previewThumbnail != null) {
- return sOverrides.previewThumbnail;
- }
- return super.loadThumbnail(uri, size);
+ protected ImageLoader createPreviewImageLoader() {
+ return new TestPreviewImageLoader(
+ super.createPreviewImageLoader(),
+ () -> sOverrides.previewThumbnail);
}
@Override
@@ -290,4 +280,12 @@ public class ChooserWrapperActivity
return super.createShortcutLoader(
context, appPredictor, userHandle, targetIntentFilter, callback);
}
+
+ @Override
+ protected FeatureFlagRepository createFeatureFlagRepository() {
+ if (sOverrides.featureFlagRepository != null) {
+ return sOverrides.featureFlagRepository;
+ }
+ return super.createFeatureFlagRepository();
+ }
}
diff --git a/java/tests/src/com/android/intentresolver/EnterTransitionAnimationDelegateTest.kt b/java/tests/src/com/android/intentresolver/EnterTransitionAnimationDelegateTest.kt
new file mode 100644
index 00000000..9ea9dfa7
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/EnterTransitionAnimationDelegateTest.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.res.Resources
+import android.view.View
+import android.view.Window
+import androidx.activity.ComponentActivity
+import androidx.lifecycle.Lifecycle
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+private const val TIMEOUT_MS = 200
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class EnterTransitionAnimationDelegateTest {
+ private val elementName = "shared-element"
+ private val scheduler = TestCoroutineScheduler()
+ private val dispatcher = StandardTestDispatcher(scheduler)
+ private val lifecycleOwner = TestLifecycleOwner()
+
+ private val transitionTargetView = mock<View> {
+ // avoid the request-layout path in the delegate
+ whenever(isInLayout).thenReturn(true)
+ }
+
+ private val windowMock = mock<Window>()
+ private val resourcesMock = mock<Resources> {
+ whenever(getInteger(anyInt())).thenReturn(TIMEOUT_MS)
+ }
+ private val activity = mock<ComponentActivity> {
+ whenever(lifecycle).thenReturn(lifecycleOwner.lifecycle)
+ whenever(resources).thenReturn(resourcesMock)
+ whenever(isActivityTransitionRunning).thenReturn(true)
+ whenever(window).thenReturn(windowMock)
+ }
+
+ private val testSubject = EnterTransitionAnimationDelegate(activity) {
+ transitionTargetView
+ }
+
+ @Before
+ fun setup() {
+ Dispatchers.setMain(dispatcher)
+ lifecycleOwner.state = Lifecycle.State.CREATED
+ }
+
+ @After
+ fun cleanup() {
+ lifecycleOwner.state = Lifecycle.State.DESTROYED
+ Dispatchers.resetMain()
+ }
+
+ @Test
+ fun test_postponeTransition_timeout() {
+ testSubject.postponeTransition()
+ testSubject.markOffsetCalculated()
+
+ scheduler.advanceTimeBy(TIMEOUT_MS + 1L)
+ verify(activity, times(1)).startPostponedEnterTransition()
+ verify(windowMock, never()).setWindowAnimations(anyInt())
+ }
+
+ @Test
+ fun test_postponeTransition_animation_resumes_only_once() {
+ testSubject.postponeTransition()
+ testSubject.markOffsetCalculated()
+ testSubject.onTransitionElementReady(elementName)
+ testSubject.markOffsetCalculated()
+ testSubject.onTransitionElementReady(elementName)
+
+ scheduler.advanceTimeBy(TIMEOUT_MS + 1L)
+ verify(activity, times(1)).startPostponedEnterTransition()
+ }
+
+ @Test
+ fun test_postponeTransition_resume_animation_conditions() {
+ testSubject.postponeTransition()
+ verify(activity, never()).startPostponedEnterTransition()
+
+ testSubject.markOffsetCalculated()
+ verify(activity, never()).startPostponedEnterTransition()
+
+ testSubject.onAllTransitionElementsReady()
+ verify(activity, times(1)).startPostponedEnterTransition()
+ }
+}
diff --git a/java/tests/src/com/android/intentresolver/FeatureFlagRule.kt b/java/tests/src/com/android/intentresolver/FeatureFlagRule.kt
new file mode 100644
index 00000000..3fa01bcc
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/FeatureFlagRule.kt
@@ -0,0 +1,56 @@
+/*
+ * 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 com.android.systemui.flags.BooleanFlag
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Ignores tests annotated with [RequireFeatureFlags] which flag requirements does not
+ * meet in the active flag set.
+ * @param flags active flag set
+ */
+internal class FeatureFlagRule(flags: Map<BooleanFlag, Boolean>) : TestRule {
+ private val flags = flags.entries.fold(HashMap<String, Boolean>()) { map, (key, value) ->
+ map.apply {
+ put(key.name, value)
+ }
+ }
+ private val skippingStatement = object : Statement() {
+ override fun evaluate() = Unit
+ }
+
+ override fun apply(base: Statement, description: Description): Statement {
+ val annotation = description.annotations.firstOrNull {
+ it is RequireFeatureFlags
+ } as? RequireFeatureFlags
+ ?: return base
+
+ if (annotation.flags.size != annotation.values.size) {
+ error("${description.className}#${description.methodName}: inconsistent number of" +
+ " flags and values in $annotation")
+ }
+ for (i in annotation.flags.indices) {
+ val flag = annotation.flags[i]
+ val value = annotation.values[i]
+ if (flags.getOrDefault(flag, !value) != value) return skippingStatement
+ }
+ return base
+ }
+}
diff --git a/java/tests/src/com/android/intentresolver/ImagePreviewImageLoaderTest.kt b/java/tests/src/com/android/intentresolver/ImagePreviewImageLoaderTest.kt
new file mode 100644
index 00000000..f327e19e
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/ImagePreviewImageLoaderTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.ContentResolver
+import android.content.Context
+import android.content.res.Resources
+import android.net.Uri
+import android.util.Size
+import androidx.lifecycle.Lifecycle
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class ImagePreviewImageLoaderTest {
+ private val imageSize = Size(300, 300)
+ private val uriOne = Uri.parse("content://org.package.app/image-1.png")
+ private val uriTwo = Uri.parse("content://org.package.app/image-2.png")
+ private val contentResolver = mock<ContentResolver>()
+ private val resources = mock<Resources> {
+ whenever(getDimensionPixelSize(R.dimen.chooser_preview_image_max_dimen))
+ .thenReturn(imageSize.width)
+ }
+ private val context = mock<Context> {
+ whenever(this.resources).thenReturn(this@ImagePreviewImageLoaderTest.resources)
+ whenever(this.contentResolver).thenReturn(this@ImagePreviewImageLoaderTest.contentResolver)
+ }
+ private val scheduler = TestCoroutineScheduler()
+ private val lifecycleOwner = TestLifecycleOwner()
+ private val dispatcher = UnconfinedTestDispatcher(scheduler)
+ private val testSubject = ImagePreviewImageLoader(
+ context, lifecycleOwner.lifecycle, 1, dispatcher
+ )
+
+ @Before
+ fun setup() {
+ Dispatchers.setMain(dispatcher)
+ lifecycleOwner.state = Lifecycle.State.CREATED
+ }
+
+ @After
+ fun cleanup() {
+ lifecycleOwner.state = Lifecycle.State.DESTROYED
+ Dispatchers.resetMain()
+ }
+
+ @Test
+ fun test_prePopulate() = runTest {
+ testSubject.prePopulate(listOf(uriOne, uriTwo))
+
+ verify(contentResolver, times(1)).loadThumbnail(uriOne, imageSize, null)
+ verify(contentResolver, never()).loadThumbnail(uriTwo, imageSize, null)
+
+ testSubject(uriOne)
+ verify(contentResolver, times(1)).loadThumbnail(uriOne, imageSize, null)
+ }
+
+ @Test
+ fun test_invoke_return_cached_image() = runTest {
+ testSubject(uriOne)
+ testSubject(uriOne)
+
+ verify(contentResolver, times(1)).loadThumbnail(any(), any(), anyOrNull())
+ }
+
+ @Test
+ fun test_invoke_old_records_evicted_from_the_cache() = runTest {
+ testSubject(uriOne)
+ testSubject(uriTwo)
+ testSubject(uriTwo)
+ testSubject(uriOne)
+
+ verify(contentResolver, times(2)).loadThumbnail(uriOne, imageSize, null)
+ verify(contentResolver, times(1)).loadThumbnail(uriTwo, imageSize, null)
+ }
+}
diff --git a/java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt b/java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt
index 159c6d6a..aaa7a282 100644
--- a/java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt
+++ b/java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt
@@ -26,6 +26,7 @@ package com.android.intentresolver
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatcher
+import org.mockito.ArgumentMatchers
import org.mockito.Mockito
import org.mockito.stubbing.OngoingStubbing
@@ -144,3 +145,5 @@ inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() ->
*/
inline fun <reified T : Any> captureMany(block: KotlinArgumentCaptor<T>.() -> Unit): List<T> =
kotlinArgumentCaptor<T>().apply{ block() }.allValues
+
+inline fun <reified T> anyOrNull() = ArgumentMatchers.argThat(ArgumentMatcher<T?> { true })
diff --git a/java/tests/src/com/android/intentresolver/RequireFeatureFlags.kt b/java/tests/src/com/android/intentresolver/RequireFeatureFlags.kt
new file mode 100644
index 00000000..1ddf7462
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/RequireFeatureFlags.kt
@@ -0,0 +1,23 @@
+/*
+ * 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
+
+/**
+ * Specifies expected feature flag values for a test.
+ */
+@Target(AnnotationTarget.FUNCTION)
+annotation class RequireFeatureFlags(val flags: Array<String>, val values: BooleanArray)
diff --git a/java/tests/src/com/android/intentresolver/ResolverActivityTest.java b/java/tests/src/com/android/intentresolver/ResolverActivityTest.java
index 62c16ff5..ae1b99f8 100644
--- a/java/tests/src/com/android/intentresolver/ResolverActivityTest.java
+++ b/java/tests/src/com/android/intentresolver/ResolverActivityTest.java
@@ -54,7 +54,6 @@ import androidx.test.espresso.NoMatchingViewException;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
-import com.android.intentresolver.ResolverActivity.ResolvedComponentInfo;
import com.android.intentresolver.widget.ResolverDrawerLayout;
import com.android.internal.R;
@@ -101,10 +100,7 @@ public class ResolverActivityTest {
Intent sendIntent = createSendImageIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource());
@@ -133,10 +129,7 @@ public class ResolverActivityTest {
Intent sendIntent = createSendImageIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
waitForIdle();
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
@@ -178,10 +171,7 @@ public class ResolverActivityTest {
Intent sendIntent = createSendImageIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
waitForIdle();
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
@@ -211,10 +201,7 @@ public class ResolverActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
when(sOverrides.resolverListController.getLastChosen())
.thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
@@ -277,10 +264,7 @@ public class ResolverActivityTest {
createResolvedComponentsForTestWithOtherProfile(3);
ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource());
@@ -319,10 +303,7 @@ public class ResolverActivityTest {
createResolvedComponentsForTestWithOtherProfile(3);
ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
when(sOverrides.resolverListController.getLastChosen())
.thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0));
@@ -761,10 +742,7 @@ public class ResolverActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos =
createResolvedComponentsForTest(2);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
when(sOverrides.resolverListController.getLastChosen())
.thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0));
@@ -837,22 +815,33 @@ public class ResolverActivityTest {
}
private void setupResolverControllers(
+ List<ResolvedComponentInfo> personalResolvedComponentInfos) {
+ setupResolverControllers(personalResolvedComponentInfos, new ArrayList<>());
+ }
+
+ private void setupResolverControllers(
List<ResolvedComponentInfo> personalResolvedComponentInfos,
List<ResolvedComponentInfo> workResolvedComponentInfos) {
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ when(sOverrides.resolverListController.getResolversForIntentAsUser(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.isA(List.class),
+ eq(UserHandle.SYSTEM)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntentAsUser(
Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntentAsUser(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class),
+ eq(UserHandle.of(10))))
+ .thenReturn(new ArrayList<>(workResolvedComponentInfos));
}
}
diff --git a/java/tests/src/com/android/intentresolver/ResolverDataProvider.java b/java/tests/src/com/android/intentresolver/ResolverDataProvider.java
index fb928e09..b6b32b5a 100644
--- a/java/tests/src/com/android/intentresolver/ResolverDataProvider.java
+++ b/java/tests/src/com/android/intentresolver/ResolverDataProvider.java
@@ -36,20 +36,33 @@ public class ResolverDataProvider {
static private int USER_SOMEONE_ELSE = 10;
- static ResolverActivity.ResolvedComponentInfo createResolvedComponentInfo(int i) {
- return new ResolverActivity.ResolvedComponentInfo(createComponentName(i),
- createResolverIntent(i), createResolveInfo(i, UserHandle.USER_CURRENT));
+ static ResolvedComponentInfo createResolvedComponentInfo(int i) {
+ return new ResolvedComponentInfo(
+ createComponentName(i),
+ createResolverIntent(i),
+ createResolveInfo(i, UserHandle.USER_CURRENT));
}
- static ResolverActivity.ResolvedComponentInfo createResolvedComponentInfoWithOtherId(int i) {
- return new ResolverActivity.ResolvedComponentInfo(createComponentName(i),
- createResolverIntent(i), createResolveInfo(i, USER_SOMEONE_ELSE));
+ static ResolvedComponentInfo createResolvedComponentInfo(
+ ComponentName componentName, Intent intent) {
+ return new ResolvedComponentInfo(
+ componentName,
+ intent,
+ createResolveInfo(componentName, UserHandle.USER_CURRENT));
}
- static ResolverActivity.ResolvedComponentInfo createResolvedComponentInfoWithOtherId(int i,
- int userId) {
- return new ResolverActivity.ResolvedComponentInfo(createComponentName(i),
- createResolverIntent(i), createResolveInfo(i, userId));
+ static ResolvedComponentInfo createResolvedComponentInfoWithOtherId(int i) {
+ return new ResolvedComponentInfo(
+ createComponentName(i),
+ createResolverIntent(i),
+ createResolveInfo(i, USER_SOMEONE_ELSE));
+ }
+
+ static ResolvedComponentInfo createResolvedComponentInfoWithOtherId(int i, int userId) {
+ return new ResolvedComponentInfo(
+ createComponentName(i),
+ createResolverIntent(i),
+ createResolveInfo(i, userId));
}
public static ComponentName createComponentName(int i) {
@@ -64,6 +77,13 @@ public class ResolverDataProvider {
return resolveInfo;
}
+ public static ResolveInfo createResolveInfo(ComponentName componentName, int userId) {
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = createActivityInfo(componentName);
+ resolveInfo.targetUserId = userId;
+ return resolveInfo;
+ }
+
static ActivityInfo createActivityInfo(int i) {
ActivityInfo ai = new ActivityInfo();
ai.name = "activity_name" + i;
@@ -75,6 +95,18 @@ public class ResolverDataProvider {
return ai;
}
+ static ActivityInfo createActivityInfo(ComponentName componentName) {
+ ActivityInfo ai = new ActivityInfo();
+ ai.name = componentName.getClassName();
+ ai.packageName = componentName.getPackageName();
+ ai.enabled = true;
+ ai.exported = true;
+ ai.permission = null;
+ ai.applicationInfo = createApplicationInfo();
+ ai.applicationInfo.packageName = componentName.getPackageName();
+ return ai;
+ }
+
static ApplicationInfo createApplicationInfo() {
ApplicationInfo ai = new ApplicationInfo();
ai.name = "app_name";
diff --git a/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java b/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java
index 239bffe0..d67b73af 100644
--- a/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java
+++ b/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java
@@ -31,7 +31,6 @@ import android.os.UserHandle;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
import com.android.intentresolver.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
-import com.android.intentresolver.AbstractMultiProfilePagerAdapter.QuietModeManager;
import com.android.intentresolver.chooser.TargetInfo;
import java.util.List;
@@ -88,11 +87,11 @@ public class ResolverWrapperActivity extends ResolverActivity {
}
@Override
- protected QuietModeManager createQuietModeManager() {
- if (sOverrides.mQuietModeManager != null) {
- return sOverrides.mQuietModeManager;
+ protected WorkProfileAvailabilityManager createWorkProfileAvailabilityManager() {
+ if (sOverrides.mWorkProfileAvailability != null) {
+ return sOverrides.mWorkProfileAvailability;
}
- return super.createQuietModeManager();
+ return super.createWorkProfileAvailabilityManager();
}
ResolverWrapperAdapter getAdapter() {
@@ -130,10 +129,8 @@ public class ResolverWrapperActivity extends ResolverActivity {
@Override
protected ResolverListController createListController(UserHandle userHandle) {
if (userHandle == UserHandle.SYSTEM) {
- when(sOverrides.resolverListController.getUserHandle()).thenReturn(UserHandle.SYSTEM);
return sOverrides.resolverListController;
}
- when(sOverrides.workResolverListController.getUserHandle()).thenReturn(userHandle);
return sOverrides.workResolverListController;
}
@@ -175,7 +172,7 @@ public class ResolverWrapperActivity extends ResolverActivity {
public Integer myUserId;
public boolean hasCrossProfileIntents;
public boolean isQuietModeEnabled;
- public QuietModeManager mQuietModeManager;
+ public WorkProfileAvailabilityManager mWorkProfileAvailability;
public MyUserIdProvider mMyUserIdProvider;
public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
@@ -190,23 +187,26 @@ public class ResolverWrapperActivity extends ResolverActivity {
hasCrossProfileIntents = true;
isQuietModeEnabled = false;
- mQuietModeManager = new QuietModeManager() {
+ mWorkProfileAvailability = new WorkProfileAvailabilityManager(null, null, null) {
@Override
- public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ public boolean isQuietModeEnabled() {
return isQuietModeEnabled;
}
@Override
- public void requestQuietModeEnabled(boolean enabled,
- UserHandle workProfileUserHandle) {
- isQuietModeEnabled = enabled;
+ public boolean isWorkProfileUserUnlocked() {
+ return true;
}
@Override
- public void markWorkProfileEnabledBroadcastReceived() {
+ public void requestQuietModeEnabled(boolean enabled) {
+ isQuietModeEnabled = enabled;
}
@Override
+ public void markWorkProfileEnabledBroadcastReceived() {}
+
+ @Override
public boolean isWaitingToEnableWorkProfile() {
return false;
}
diff --git a/java/tests/src/com/android/intentresolver/TestFeatureFlagRepository.kt b/java/tests/src/com/android/intentresolver/TestFeatureFlagRepository.kt
new file mode 100644
index 00000000..b9047712
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/TestFeatureFlagRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * 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 com.android.intentresolver.flags.FeatureFlagRepository
+import com.android.systemui.flags.BooleanFlag
+import com.android.systemui.flags.ReleasedFlag
+import com.android.systemui.flags.UnreleasedFlag
+
+class TestFeatureFlagRepository(
+ private val overrides: Map<BooleanFlag, Boolean>
+) : FeatureFlagRepository {
+ override fun isEnabled(flag: UnreleasedFlag): Boolean = getValue(flag)
+ override fun isEnabled(flag: ReleasedFlag): Boolean = getValue(flag)
+
+ private fun getValue(flag: BooleanFlag) = overrides.getOrDefault(flag, flag.default)
+}
diff --git a/java/tests/src/com/android/intentresolver/TestLifecycleOwner.kt b/java/tests/src/com/android/intentresolver/TestLifecycleOwner.kt
new file mode 100644
index 00000000..f47e343f
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/TestLifecycleOwner.kt
@@ -0,0 +1,33 @@
+/*
+ * 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 androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+
+internal class TestLifecycleOwner : LifecycleOwner {
+ private val lifecycleRegistry = LifecycleRegistry.createUnsafe(this)
+
+ override fun getLifecycle(): Lifecycle = lifecycleRegistry
+
+ var state: Lifecycle.State
+ get() = lifecycle.currentState
+ set(value) {
+ lifecycleRegistry.currentState = value
+ }
+} \ No newline at end of file
diff --git a/java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt b/java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt
new file mode 100644
index 00000000..cfe041dd
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.graphics.Bitmap
+import android.net.Uri
+import java.util.function.Consumer
+
+internal class TestPreviewImageLoader(
+ private val imageLoader: ImageLoader,
+ private val imageOverride: () -> Bitmap?
+) : ImageLoader {
+ override fun loadImage(uri: Uri, callback: Consumer<Bitmap?>) {
+ val override = imageOverride()
+ if (override != null) {
+ callback.accept(override)
+ } else {
+ imageLoader.loadImage(uri, callback)
+ }
+ }
+
+ override suspend fun invoke(uri: Uri): Bitmap? = imageOverride() ?: imageLoader(uri)
+ override fun prePopulate(uris: List<Uri>) = Unit
+}
diff --git a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
index af2557ef..9ffd02d4 100644
--- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
+++ b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
@@ -20,6 +20,7 @@ import static android.app.Activity.RESULT_OK;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
@@ -55,13 +56,16 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.PendingIntent;
import android.app.usage.UsageStatsManager;
+import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -69,6 +73,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -79,25 +84,29 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.service.chooser.ChooserAction;
import android.service.chooser.ChooserTarget;
import android.util.HashedStringCache;
import android.util.Pair;
import android.util.SparseArray;
import android.view.View;
+import android.view.ViewGroup;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.espresso.contrib.RecyclerViewActions;
import androidx.test.espresso.matcher.BoundedDiagnosingMatcher;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
-import com.android.intentresolver.ResolverActivity.ResolvedComponentInfo;
import com.android.intentresolver.chooser.DisplayResolveInfo;
+import com.android.intentresolver.flags.Flags;
import com.android.intentresolver.shortcuts.ShortcutLoader;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.flags.BooleanFlag;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
@@ -106,6 +115,8 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.mockito.ArgumentCaptor;
@@ -117,6 +128,8 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -150,14 +163,40 @@ public class UnbundledChooserActivityTest {
return mock;
};
+ private static final List<BooleanFlag> ALL_FLAGS =
+ Arrays.asList(
+ Flags.SHARESHEET_CUSTOM_ACTIONS,
+ Flags.SHARESHEET_RESELECTION_ACTION,
+ Flags.SHARESHEET_IMAGE_AND_TEXT_PREVIEW,
+ Flags.SHARESHEET_SCROLLABLE_IMAGE_PREVIEW);
+
+ private static final Map<BooleanFlag, Boolean> ALL_FLAGS_OFF =
+ createAllFlagsOverride(false);
+ private static final Map<BooleanFlag, Boolean> ALL_FLAGS_ON =
+ createAllFlagsOverride(true);
+
@Parameterized.Parameters
public static Collection packageManagers() {
return Arrays.asList(new Object[][] {
- {0, "Default PackageManager", DEFAULT_PM},
- {1, "No App Prediction Service", NO_APP_PREDICTION_SERVICE_PM}
+ // Default PackageManager and all flags off
+ { DEFAULT_PM, ALL_FLAGS_OFF },
+ // Default PackageManager and all flags on
+ { DEFAULT_PM, ALL_FLAGS_ON },
+ // No App Prediction Service and all flags off
+ { NO_APP_PREDICTION_SERVICE_PM, ALL_FLAGS_OFF },
+ // No App Prediction Service and all flags on
+ { NO_APP_PREDICTION_SERVICE_PM, ALL_FLAGS_ON }
});
}
+ private static Map<BooleanFlag, Boolean> createAllFlagsOverride(boolean value) {
+ HashMap<BooleanFlag, Boolean> overrides = new HashMap<>(ALL_FLAGS.size());
+ for (BooleanFlag flag : ALL_FLAGS) {
+ overrides.put(flag, value);
+ }
+ return overrides;
+ }
+
/* --------
* Subclasses can override the following methods to customize test behavior.
* --------
@@ -177,6 +216,8 @@ public class UnbundledChooserActivityTest {
.adoptShellPermissionIdentity();
cleanOverrideData();
+ ChooserActivityOverrideData.getInstance().featureFlagRepository =
+ new TestFeatureFlagRepository(mFlags);
}
/**
@@ -209,11 +250,13 @@ public class UnbundledChooserActivityTest {
* --------
*/
+ @Rule
+ public final TestRule mRule;
+
// Shared test code references the activity under test as ChooserActivity, the common ancestor
// of any (inheritance-based) chooser implementation. For testing purposes, that activity will
// usually be cast to IChooserWrapper to expose instrumentation.
- @Rule
- public ActivityTestRule<ChooserActivity> mActivityRule =
+ private ActivityTestRule<ChooserActivity> mActivityRule =
new ActivityTestRule<>(ChooserActivity.class, false, false) {
@Override
public ChooserActivity launchActivity(Intent clientIntent) {
@@ -240,16 +283,20 @@ public class UnbundledChooserActivityTest {
private static final int CONTENT_PREVIEW_IMAGE = 1;
private static final int CONTENT_PREVIEW_FILE = 2;
private static final int CONTENT_PREVIEW_TEXT = 3;
- private Function<PackageManager, PackageManager> mPackageManagerOverride;
- private int mTestNum;
+
+ private final Function<PackageManager, PackageManager> mPackageManagerOverride;
+ private final Map<BooleanFlag, Boolean> mFlags;
public UnbundledChooserActivityTest(
- int testNum,
- String testName,
- Function<PackageManager, PackageManager> packageManagerOverride) {
+ Function<PackageManager, PackageManager> packageManagerOverride,
+ Map<BooleanFlag, Boolean> flags) {
mPackageManagerOverride = packageManagerOverride;
- mTestNum = testNum;
+ mFlags = flags;
+
+ mRule = RuleChain
+ .outerRule(new FeatureFlagRule(flags))
+ .around(mActivityRule);
}
private void setDeviceConfigProperty(
@@ -284,16 +331,7 @@ public class UnbundledChooserActivityTest {
Intent viewIntent = createViewTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(
Intent.createChooser(viewIntent, "chooser test"));
@@ -308,16 +346,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "chooser test"));
waitForIdle();
onView(withId(android.R.id.title))
@@ -329,16 +358,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(android.R.id.title))
@@ -350,16 +370,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntentWithPreview(null, null);
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(com.android.internal.R.id.content_preview_title))
@@ -374,16 +385,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntentWithPreview(previewTitle, null);
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(com.android.internal.R.id.content_preview_title))
@@ -401,16 +403,7 @@ public class UnbundledChooserActivityTest {
Uri.parse("tel:(+49)12345789"));
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(com.android.internal.R.id.content_preview_title))
@@ -428,16 +421,7 @@ public class UnbundledChooserActivityTest {
ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(com.android.internal.R.id.content_preview_title))
@@ -451,16 +435,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -506,16 +481,7 @@ public class UnbundledChooserActivityTest {
}
resolvedComponentInfos.addAll(infosToStack);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -547,16 +513,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -582,16 +539,7 @@ public class UnbundledChooserActivityTest {
@Ignore // b/148158199
@Test
public void noResultsFromPackageManager() {
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(null);
+ setupResolverControllers(null);
Intent sendIntent = createSendTextIntent();
final ChooserActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -618,16 +566,7 @@ public class UnbundledChooserActivityTest {
};
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
final ChooserActivity activity =
@@ -679,11 +618,7 @@ public class UnbundledChooserActivityTest {
createResolvedComponentsForTestWithOtherProfile(3);
ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
- when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
when(ChooserActivityOverrideData.getInstance().resolverListController.getLastChosen())
.thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
@@ -716,11 +651,7 @@ public class UnbundledChooserActivityTest {
createResolvedComponentsForTestWithOtherProfile(3);
ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
- when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -745,15 +676,148 @@ public class UnbundledChooserActivityTest {
}
@Test
+ @RequireFeatureFlags(
+ flags = { Flags.SHARESHEET_IMAGE_AND_TEXT_PREVIEW_NAME },
+ values = { true })
+ public void testImagePlusTextSharing_ExcludeText() {
+ Intent sendIntent = createSendImageIntent(
+ Uri.parse("android.resource://com.android.frameworks.coretests/"
+ + R.drawable.test320x240));
+ ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
+ ChooserActivityOverrideData.getInstance().isImageType = true;
+ sendIntent.putExtra(Intent.EXTRA_TEXT, "https://google.com/search?q=google");
+
+ List<ResolvedComponentInfo> resolvedComponentInfos = Arrays.asList(
+ ResolverDataProvider.createResolvedComponentInfo(
+ new ComponentName("org.imageviewer", "ImageTarget"),
+ sendIntent),
+ ResolverDataProvider.createResolvedComponentInfo(
+ new ComponentName("org.textviewer", "UriTarget"),
+ new Intent("VIEW_TEXT"))
+ );
+
+ setupResolverControllers(resolvedComponentInfos);
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ onView(withId(R.id.include_text_action))
+ .check(matches(isDisplayed()))
+ .perform(click());
+ waitForIdle();
+
+ AtomicReference<Intent> launchedIntentRef = new AtomicReference<>();
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
+ launchedIntentRef.set(targetInfo.getTargetIntent());
+ return true;
+ };
+
+ onView(withText(resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.name))
+ .perform(click());
+ waitForIdle();
+ assertThat(launchedIntentRef.get().hasExtra(Intent.EXTRA_TEXT)).isFalse();
+ }
+
+ @Test
+ @RequireFeatureFlags(
+ flags = { Flags.SHARESHEET_IMAGE_AND_TEXT_PREVIEW_NAME },
+ values = { true })
+ public void testImagePlusTextSharing_RemoveAndAddBackText() {
+ Intent sendIntent = createSendImageIntent(
+ Uri.parse("android.resource://com.android.frameworks.coretests/"
+ + R.drawable.test320x240));
+ ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
+ ChooserActivityOverrideData.getInstance().isImageType = true;
+ final String text = "https://google.com/search?q=google";
+ sendIntent.putExtra(Intent.EXTRA_TEXT, text);
+
+ List<ResolvedComponentInfo> resolvedComponentInfos = Arrays.asList(
+ ResolverDataProvider.createResolvedComponentInfo(
+ new ComponentName("org.imageviewer", "ImageTarget"),
+ sendIntent),
+ ResolverDataProvider.createResolvedComponentInfo(
+ new ComponentName("org.textviewer", "UriTarget"),
+ new Intent("VIEW_TEXT"))
+ );
+
+ setupResolverControllers(resolvedComponentInfos);
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ onView(withId(R.id.include_text_action))
+ .check(matches(isDisplayed()))
+ .perform(click());
+ waitForIdle();
+ onView(withId(R.id.include_text_action))
+ .perform(click());
+ waitForIdle();
+
+ AtomicReference<Intent> launchedIntentRef = new AtomicReference<>();
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
+ launchedIntentRef.set(targetInfo.getTargetIntent());
+ return true;
+ };
+
+ onView(withText(resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.name))
+ .perform(click());
+ waitForIdle();
+ assertThat(launchedIntentRef.get().getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(text);
+ }
+
+ @Test
+ @RequireFeatureFlags(
+ flags = { Flags.SHARESHEET_IMAGE_AND_TEXT_PREVIEW_NAME },
+ values = { true })
+ public void testImagePlusTextSharing_TextExclusionDoesNotAffectAlternativeIntent() {
+ Intent sendIntent = createSendImageIntent(
+ Uri.parse("android.resource://com.android.frameworks.coretests/"
+ + R.drawable.test320x240));
+ ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
+ ChooserActivityOverrideData.getInstance().isImageType = true;
+ sendIntent.putExtra(Intent.EXTRA_TEXT, "https://google.com/search?q=google");
+
+ Intent alternativeIntent = createSendTextIntent();
+ final String text = "alternative intent";
+ alternativeIntent.putExtra(Intent.EXTRA_TEXT, text);
+
+ List<ResolvedComponentInfo> resolvedComponentInfos = Arrays.asList(
+ ResolverDataProvider.createResolvedComponentInfo(
+ new ComponentName("org.imageviewer", "ImageTarget"),
+ sendIntent),
+ ResolverDataProvider.createResolvedComponentInfo(
+ new ComponentName("org.textviewer", "UriTarget"),
+ alternativeIntent)
+ );
+
+ setupResolverControllers(resolvedComponentInfos);
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ onView(withId(R.id.include_text_action))
+ .check(matches(isDisplayed()))
+ .perform(click());
+ waitForIdle();
+
+ AtomicReference<Intent> launchedIntentRef = new AtomicReference<>();
+ ChooserActivityOverrideData.getInstance().onSafelyStartCallback = targetInfo -> {
+ launchedIntentRef.set(targetInfo.getTargetIntent());
+ return true;
+ };
+
+ onView(withText(resolvedComponentInfos.get(1).getResolveInfoAt(0).activityInfo.name))
+ .perform(click());
+ waitForIdle();
+ assertThat(launchedIntentRef.get().getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(text);
+ }
+
+ @Test
public void copyTextToClipboard() throws Exception {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final ChooserActivity activity =
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -777,11 +841,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -800,11 +860,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -830,11 +886,7 @@ public class UnbundledChooserActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -848,7 +900,7 @@ public class UnbundledChooserActivityTest {
@Test
- public void oneVisibleImagePreview() throws InterruptedException {
+ public void oneVisibleImagePreview() {
Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ R.drawable.test320x240);
@@ -861,30 +913,34 @@ public class UnbundledChooserActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(com.android.internal.R.id.content_preview_image_1_large))
- .check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.content_preview_image_2_large))
- .check(matches(not(isDisplayed())));
- onView(withId(com.android.internal.R.id.content_preview_image_2_small))
- .check(matches(not(isDisplayed())));
- onView(withId(com.android.internal.R.id.content_preview_image_3_small))
- .check(matches(not(isDisplayed())));
+ onView(withId(com.android.internal.R.id.content_preview_image_area))
+ .check((view, exception) -> {
+ if (exception != null) {
+ throw exception;
+ }
+ ViewGroup parent = (ViewGroup) view;
+ ArrayList<View> visibleViews = new ArrayList<>();
+ for (int i = 0, count = parent.getChildCount(); i < count; i++) {
+ View child = parent.getChildAt(i);
+ if (child.getVisibility() == View.VISIBLE) {
+ visibleViews.add(child);
+ }
+ }
+ assertThat(visibleViews.size(), is(1));
+ assertThat(
+ "image preview view is fully visible",
+ isDisplayed().matches(visibleViews.get(0)));
+ });
}
@Test
- public void twoVisibleImagePreview() throws InterruptedException {
+ @RequireFeatureFlags(
+ flags = { Flags.SHARESHEET_SCROLLABLE_IMAGE_PREVIEW_NAME },
+ values = { false })
+ public void twoVisibleImagePreview() {
Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ R.drawable.test320x240);
@@ -898,16 +954,7 @@ public class UnbundledChooserActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(com.android.internal.R.id.content_preview_image_1_large))
@@ -921,7 +968,10 @@ public class UnbundledChooserActivityTest {
}
@Test
- public void threeOrMoreVisibleImagePreview() throws InterruptedException {
+ @RequireFeatureFlags(
+ flags = { Flags.SHARESHEET_SCROLLABLE_IMAGE_PREVIEW_NAME },
+ values = { false })
+ public void threeOrMoreVisibleImagePreview() {
Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ R.drawable.test320x240);
@@ -938,16 +988,7 @@ public class UnbundledChooserActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(com.android.internal.R.id.content_preview_image_1_large))
@@ -961,6 +1002,72 @@ public class UnbundledChooserActivityTest {
}
@Test
+ @RequireFeatureFlags(
+ flags = { Flags.SHARESHEET_SCROLLABLE_IMAGE_PREVIEW_NAME },
+ values = { true })
+ public void testManyVisibleImagePreview_ScrollableImagePreview() {
+ Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ + R.drawable.test320x240);
+
+ ArrayList<Uri> uris = new ArrayList<>();
+ uris.add(uri);
+ uris.add(uri);
+ uris.add(uri);
+ uris.add(uri);
+ uris.add(uri);
+ uris.add(uri);
+ uris.add(uri);
+ uris.add(uri);
+ uris.add(uri);
+ uris.add(uri);
+
+ Intent sendIntent = createSendUriIntentWithPreview(uris);
+ ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
+ ChooserActivityOverrideData.getInstance().isImageType = true;
+
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ setupResolverControllers(resolvedComponentInfos);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ onView(withId(com.android.internal.R.id.content_preview_image_area))
+ .perform(RecyclerViewActions.scrollToLastPosition())
+ .check((view, exception) -> {
+ if (exception != null) {
+ throw exception;
+ }
+ RecyclerView recyclerView = (RecyclerView) view;
+ assertThat(recyclerView.getAdapter().getItemCount(), is(uris.size()));
+ });
+ }
+
+ @Test
+ @RequireFeatureFlags(
+ flags = { Flags.SHARESHEET_IMAGE_AND_TEXT_PREVIEW_NAME },
+ values = { true })
+ public void testImageAndTextPreview() {
+ final Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ + R.drawable.test320x240);
+ final String sharedText = "text-" + System.currentTimeMillis();
+
+ ArrayList<Uri> uris = new ArrayList<>();
+ uris.add(uri);
+
+ Intent sendIntent = createSendUriIntentWithPreview(uris);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText);
+ ChooserActivityOverrideData.getInstance().previewThumbnail = createBitmap();
+ ChooserActivityOverrideData.getInstance().isImageType = true;
+
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ setupResolverControllers(resolvedComponentInfos);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ onView(withText(sharedText))
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
public void testOnCreateLogging() {
Intent sendIntent = createSendTextIntent();
sendIntent.setType(TEST_MIME_TYPE);
@@ -1007,11 +1114,7 @@ public class UnbundledChooserActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -1036,16 +1139,7 @@ public class UnbundledChooserActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -1065,16 +1159,7 @@ public class UnbundledChooserActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(com.android.internal.R.id.content_preview_filename))
@@ -1099,16 +1184,7 @@ public class UnbundledChooserActivityTest {
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
onView(withId(com.android.internal.R.id.content_preview_filename))
@@ -1129,16 +1205,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendUriIntentWithPreview(uris);
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
ChooserActivityOverrideData.getInstance().resolverForceException = true;
@@ -1163,16 +1230,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendUriIntentWithPreview(uris);
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
Cursor cursor = mock(Cursor.class);
when(cursor.getCount()).thenReturn(1);
@@ -1199,16 +1257,8 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
+
when(
ChooserActivityOverrideData
.getInstance()
@@ -1241,16 +1291,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
// create test shortcut loader factory, remember loaders and their callbacks
SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders =
@@ -1331,16 +1372,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
// create test shortcut loader factory, remember loaders and their callbacks
SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders =
@@ -1425,16 +1457,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
// create test shortcut loader factory, remember loaders and their callbacks
SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders =
@@ -1509,16 +1532,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
// create test shortcut loader factory, remember loaders and their callbacks
SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders =
@@ -1597,16 +1611,7 @@ public class UnbundledChooserActivityTest {
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
// set caller-provided target
Intent chooserIntent = Intent.createChooser(createSendTextIntent(), null);
@@ -1665,6 +1670,91 @@ public class UnbundledChooserActivityTest {
}
@Test
+ @RequireFeatureFlags(
+ flags = { Flags.SHARESHEET_CUSTOM_ACTIONS_NAME },
+ values = { true })
+ public void testLaunchWithCustomAction() throws InterruptedException {
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ setupResolverControllers(resolvedComponentInfos);
+
+ Context testContext = InstrumentationRegistry.getInstrumentation().getContext();
+ final String customActionLabel = "Custom Action";
+ final String testAction = "test-broadcast-receiver-action";
+ Intent chooserIntent = Intent.createChooser(createSendTextIntent(), null);
+ chooserIntent.putExtra(
+ Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS,
+ new ChooserAction[] {
+ new ChooserAction.Builder(
+ Icon.createWithResource("", Resources.ID_NULL),
+ customActionLabel,
+ PendingIntent.getBroadcast(
+ testContext,
+ 123,
+ new Intent(testAction),
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT))
+ .build()
+ });
+ // Start activity
+ mActivityRule.launchActivity(chooserIntent);
+ waitForIdle();
+
+ final CountDownLatch broadcastInvoked = new CountDownLatch(1);
+ BroadcastReceiver testReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ broadcastInvoked.countDown();
+ }
+ };
+ testContext.registerReceiver(testReceiver, new IntentFilter(testAction));
+
+ try {
+ onView(withText(customActionLabel)).perform(click());
+ broadcastInvoked.await();
+ } finally {
+ testContext.unregisterReceiver(testReceiver);
+ }
+ }
+
+ @Test
+ @RequireFeatureFlags(
+ flags = { Flags.SHARESHEET_RESELECTION_ACTION_NAME },
+ values = { true })
+ public void testLaunchWithShareModification() throws InterruptedException {
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ setupResolverControllers(resolvedComponentInfos);
+
+ Context testContext = InstrumentationRegistry.getInstrumentation().getContext();
+ final String modifyShareAction = "test-broadcast-receiver-action";
+ Intent chooserIntent = Intent.createChooser(createSendTextIntent(), null);
+ chooserIntent.putExtra(
+ Intent.EXTRA_CHOOSER_MODIFY_SHARE_ACTION,
+ PendingIntent.getBroadcast(
+ testContext,
+ 123,
+ new Intent(modifyShareAction),
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT));
+ // Start activity
+ mActivityRule.launchActivity(chooserIntent);
+ waitForIdle();
+
+ final CountDownLatch broadcastInvoked = new CountDownLatch(1);
+ BroadcastReceiver testReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ broadcastInvoked.countDown();
+ }
+ };
+ testContext.registerReceiver(testReceiver, new IntentFilter(modifyShareAction));
+
+ try {
+ onView(withText(R.string.select_text)).perform(click());
+ broadcastInvoked.await();
+ } finally {
+ testContext.unregisterReceiver(testReceiver);
+ }
+ }
+
+ @Test
public void testUpdateMaxTargetsPerRow_columnCountIsUpdated() throws InterruptedException {
updateMaxTargetsPerRowResource(/* targetsPerRow= */ 4);
givenAppTargets(/* appCount= */ 16);
@@ -1715,16 +1805,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(15);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
// Create direct share target
List<ChooserTarget> serviceTargets = createDirectShareTargets(1,
@@ -2004,16 +2085,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -2045,16 +2117,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
// create test shortcut loader factory, remember loaders and their callbacks
SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders =
@@ -2130,21 +2193,69 @@ public class UnbundledChooserActivityTest {
/* selectionCost= */ anyLong());
}
+ @Test
+ public void testDirectTargetPinningDialog() {
+ Intent sendIntent = createSendTextIntent();
+ // We need app targets for direct targets to get displayed
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ setupResolverControllers(resolvedComponentInfos);
+
+ // create test shortcut loader factory, remember loaders and their callbacks
+ SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders =
+ new SparseArray<>();
+ ChooserActivityOverrideData.getInstance().shortcutLoaderFactory =
+ (userHandle, callback) -> {
+ Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>> pair =
+ new Pair<>(mock(ShortcutLoader.class), callback);
+ shortcutLoaders.put(userHandle.getIdentifier(), pair);
+ return pair.first;
+ };
+
+ // Start activity
+ final IChooserWrapper activity = (IChooserWrapper)
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ // verify that ShortcutLoader was queried
+ ArgumentCaptor<DisplayResolveInfo[]> appTargets =
+ ArgumentCaptor.forClass(DisplayResolveInfo[].class);
+ verify(shortcutLoaders.get(0).first, times(1))
+ .queryShortcuts(appTargets.capture());
+
+ // send shortcuts
+ List<ChooserTarget> serviceTargets = createDirectShareTargets(
+ 1,
+ resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
+ ShortcutLoader.Result result = new ShortcutLoader.Result(
+ // TODO: test another value as well
+ false,
+ appTargets.getValue(),
+ new ShortcutLoader.ShortcutResultInfo[] {
+ new ShortcutLoader.ShortcutResultInfo(
+ appTargets.getValue()[0],
+ serviceTargets
+ )
+ },
+ new HashMap<>(),
+ new HashMap<>()
+ );
+ activity.getMainExecutor().execute(() -> shortcutLoaders.get(0).second.accept(result));
+ waitForIdle();
+
+ // Long-click on the direct target
+ String name = serviceTargets.get(0).getTitle().toString();
+ onView(withText(name)).perform(longClick());
+ waitForIdle();
+
+ onView(withId(R.id.chooser_dialog_content)).check(matches(isDisplayed()));
+ }
+
@Test @Ignore
public void testEmptyDirectRowLogging() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
// Start activity
final IChooserWrapper activity = (IChooserWrapper)
@@ -2168,16 +2279,7 @@ public class UnbundledChooserActivityTest {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -2239,16 +2341,7 @@ public class UnbundledChooserActivityTest {
public void testOneInitialIntent_noAutolaunch() {
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTest(1);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos);
Intent chooserIntent = createChooserIntent(createSendTextIntent(),
new Intent[] {new Intent("action.fake")});
ResolveInfo[] chosen = new ResolveInfo[1];
@@ -2374,12 +2467,7 @@ public class UnbundledChooserActivityTest {
// Create 4 ranked app targets.
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTest(4);
- when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos);
// Create caller target which is duplicate with one of app targets
Intent chooserIntent = createChooserIntent(createSendTextIntent(),
new Intent[] {new Intent("action.fake")});
@@ -2646,28 +2734,35 @@ public class UnbundledChooserActivityTest {
}
private void setupResolverControllers(
+ List<ResolvedComponentInfo> personalResolvedComponentInfos) {
+ setupResolverControllers(personalResolvedComponentInfos, new ArrayList<>());
+ }
+
+ private void setupResolverControllers(
List<ResolvedComponentInfo> personalResolvedComponentInfos,
List<ResolvedComponentInfo> workResolvedComponentInfos) {
when(
ChooserActivityOverrideData
.getInstance()
.resolverListController
- .getResolversForIntent(
+ .getResolversForIntentAsUser(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.anyBoolean(),
- Mockito.isA(List.class)))
+ Mockito.isA(List.class),
+ eq(UserHandle.SYSTEM)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
when(
ChooserActivityOverrideData
.getInstance()
.workResolverListController
- .getResolversForIntent(
+ .getResolversForIntentAsUser(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(workResolvedComponentInfos));
+ Mockito.isA(List.class),
+ eq(UserHandle.SYSTEM)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
when(
ChooserActivityOverrideData
.getInstance()
@@ -2677,8 +2772,8 @@ public class UnbundledChooserActivityTest {
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ eq(UserHandle.of(10))))
+ .thenReturn(new ArrayList<>(workResolvedComponentInfos));
}
private static GridRecyclerSpanCountMatcher withGridColumnCount(int columnCount) {
@@ -2717,16 +2812,7 @@ public class UnbundledChooserActivityTest {
private void givenAppTargets(int appCount) {
List<ResolvedComponentInfo> resolvedComponentInfos =
createResolvedComponentsForTest(appCount);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(resolvedComponentInfos);
+ setupResolverControllers(resolvedComponentInfos);
}
private void updateMaxTargetsPerRowResource(int targetsPerRow) {
diff --git a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java
index f1febed2..87dc1b9d 100644
--- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java
+++ b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java
@@ -48,7 +48,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.NoMatchingViewException;
import androidx.test.rule.ActivityTestRule;
-import com.android.intentresolver.ResolverActivity.ResolvedComponentInfo;
import com.android.intentresolver.UnbundledChooserActivityWorkProfileTest.TestCase.Tab;
import com.android.internal.R;
@@ -274,21 +273,27 @@ public class UnbundledChooserActivityWorkProfileTest {
private void setupResolverControllers(
List<ResolvedComponentInfo> personalResolvedComponentInfos,
List<ResolvedComponentInfo> workResolvedComponentInfos) {
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ when(sOverrides.resolverListController.getResolversForIntentAsUser(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.isA(List.class),
+ eq(UserHandle.SYSTEM)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntentAsUser(
Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntentAsUser(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class),
+ eq(WORK_USER_HANDLE)))
+ .thenReturn(new ArrayList<>(workResolvedComponentInfos));
}
private void waitForIdle() {
diff --git a/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt b/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt
new file mode 100644
index 00000000..e9c755d3
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt
@@ -0,0 +1,497 @@
+/*
+ * 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
+ *3
+ * 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.chooser
+
+import android.app.Activity
+import android.app.prediction.AppTarget
+import android.app.prediction.AppTargetId
+import android.content.ComponentName
+import android.content.Intent
+import android.content.pm.ResolveInfo
+import android.os.Bundle
+import android.os.UserHandle
+import com.android.intentresolver.createShortcutInfo
+import com.android.intentresolver.mock
+import com.android.intentresolver.ResolverActivity
+import com.android.intentresolver.ResolverDataProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class ImmutableTargetInfoTest {
+ private val resolvedIntent = Intent("resolved")
+ private val targetIntent = Intent("target")
+ private val referrerFillInIntent = Intent("referrer_fillin")
+ private val resolvedComponentName = ComponentName("resolved", "component")
+ private val chooserTargetComponentName = ComponentName("chooser", "target")
+ private val resolveInfo = ResolverDataProvider.createResolveInfo(1, 0)
+ private val displayLabel: CharSequence = "Display Label"
+ private val extendedInfo: CharSequence = "Extended Info"
+ private val displayIconHolder: TargetInfo.IconHolder = mock()
+ private val sourceIntent1 = Intent("source1")
+ private val sourceIntent2 = Intent("source2")
+ private val displayTarget1 = DisplayResolveInfo.newDisplayResolveInfo(
+ Intent("display1"),
+ ResolverDataProvider.createResolveInfo(2, 0),
+ "display1 label",
+ "display1 extended info",
+ Intent("display1_resolved"),
+ /* resolveInfoPresentationGetter= */ null)
+ private val displayTarget2 = DisplayResolveInfo.newDisplayResolveInfo(
+ Intent("display2"),
+ ResolverDataProvider.createResolveInfo(3, 0),
+ "display2 label",
+ "display2 extended info",
+ Intent("display2_resolved"),
+ /* resolveInfoPresentationGetter= */ null)
+ private val directShareShortcutInfo = createShortcutInfo(
+ "shortcutid", ResolverDataProvider.createComponentName(4), 4)
+ private val directShareAppTarget = AppTarget(
+ AppTargetId("apptargetid"),
+ "test.directshare",
+ "target",
+ UserHandle.CURRENT)
+ private val displayResolveInfo = DisplayResolveInfo.newDisplayResolveInfo(
+ Intent("displayresolve"),
+ ResolverDataProvider.createResolveInfo(5, 0),
+ "displayresolve label",
+ "displayresolve extended info",
+ Intent("display_resolved"),
+ /* resolveInfoPresentationGetter= */ null)
+ private val hashProvider: ImmutableTargetInfo.TargetHashProvider = mock()
+
+ @Test
+ fun testBasicProperties() { // Fields that are reflected back w/o logic.
+ // TODO: we could consider passing copies of all the values into the builder so that we can
+ // verify that they're not mutated (e.g. no extras added to the intents). For now that
+ // should be obvious from the implementation.
+ val info = ImmutableTargetInfo.newBuilder()
+ .setResolvedIntent(resolvedIntent)
+ .setTargetIntent(targetIntent)
+ .setReferrerFillInIntent(referrerFillInIntent)
+ .setResolvedComponentName(resolvedComponentName)
+ .setChooserTargetComponentName(chooserTargetComponentName)
+ .setResolveInfo(resolveInfo)
+ .setDisplayLabel(displayLabel)
+ .setExtendedInfo(extendedInfo)
+ .setDisplayIconHolder(displayIconHolder)
+ .setAlternateSourceIntents(listOf(sourceIntent1, sourceIntent2))
+ .setAllDisplayTargets(listOf(displayTarget1, displayTarget2))
+ .setIsSuspended(true)
+ .setIsPinned(true)
+ .setModifiedScore(42.0f)
+ .setDirectShareShortcutInfo(directShareShortcutInfo)
+ .setDirectShareAppTarget(directShareAppTarget)
+ .setDisplayResolveInfo(displayResolveInfo)
+ .setHashProvider(hashProvider)
+ .build()
+
+ assertThat(info.resolvedIntent).isEqualTo(resolvedIntent)
+ assertThat(info.targetIntent).isEqualTo(targetIntent)
+ assertThat(info.referrerFillInIntent).isEqualTo(referrerFillInIntent)
+ assertThat(info.resolvedComponentName).isEqualTo(resolvedComponentName)
+ assertThat(info.chooserTargetComponentName).isEqualTo(chooserTargetComponentName)
+ assertThat(info.resolveInfo).isEqualTo(resolveInfo)
+ assertThat(info.displayLabel).isEqualTo(displayLabel)
+ assertThat(info.extendedInfo).isEqualTo(extendedInfo)
+ assertThat(info.displayIconHolder).isEqualTo(displayIconHolder)
+ assertThat(info.allSourceIntents).containsExactly(
+ resolvedIntent, sourceIntent1, sourceIntent2)
+ assertThat(info.allDisplayTargets).containsExactly(displayTarget1, displayTarget2)
+ assertThat(info.isSuspended).isTrue()
+ assertThat(info.isPinned).isTrue()
+ assertThat(info.modifiedScore).isEqualTo(42.0f)
+ assertThat(info.directShareShortcutInfo).isEqualTo(directShareShortcutInfo)
+ assertThat(info.directShareAppTarget).isEqualTo(directShareAppTarget)
+ assertThat(info.displayResolveInfo).isEqualTo(displayResolveInfo)
+ assertThat(info.isEmptyTargetInfo).isFalse()
+ assertThat(info.isPlaceHolderTargetInfo).isFalse()
+ assertThat(info.isNotSelectableTargetInfo).isFalse()
+ assertThat(info.isSelectableTargetInfo).isFalse()
+ assertThat(info.isChooserTargetInfo).isFalse()
+ assertThat(info.isMultiDisplayResolveInfo).isFalse()
+ assertThat(info.isDisplayResolveInfo).isFalse()
+ assertThat(info.hashProvider).isEqualTo(hashProvider)
+ }
+
+ @Test
+ fun testToBuilderPreservesBasicProperties() {
+ // Note this is set up exactly as in `testBasicProperties`, but the assertions will be made
+ // against a *copy* of the object instead.
+ val infoToCopyFrom = ImmutableTargetInfo.newBuilder()
+ .setResolvedIntent(resolvedIntent)
+ .setTargetIntent(targetIntent)
+ .setReferrerFillInIntent(referrerFillInIntent)
+ .setResolvedComponentName(resolvedComponentName)
+ .setChooserTargetComponentName(chooserTargetComponentName)
+ .setResolveInfo(resolveInfo)
+ .setDisplayLabel(displayLabel)
+ .setExtendedInfo(extendedInfo)
+ .setDisplayIconHolder(displayIconHolder)
+ .setAlternateSourceIntents(listOf(sourceIntent1, sourceIntent2))
+ .setAllDisplayTargets(listOf(displayTarget1, displayTarget2))
+ .setIsSuspended(true)
+ .setIsPinned(true)
+ .setModifiedScore(42.0f)
+ .setDirectShareShortcutInfo(directShareShortcutInfo)
+ .setDirectShareAppTarget(directShareAppTarget)
+ .setDisplayResolveInfo(displayResolveInfo)
+ .setHashProvider(hashProvider)
+ .build()
+
+ val info = infoToCopyFrom.toBuilder().build()
+
+ assertThat(info.resolvedIntent).isEqualTo(resolvedIntent)
+ assertThat(info.targetIntent).isEqualTo(targetIntent)
+ assertThat(info.referrerFillInIntent).isEqualTo(referrerFillInIntent)
+ assertThat(info.resolvedComponentName).isEqualTo(resolvedComponentName)
+ assertThat(info.chooserTargetComponentName).isEqualTo(chooserTargetComponentName)
+ assertThat(info.resolveInfo).isEqualTo(resolveInfo)
+ assertThat(info.displayLabel).isEqualTo(displayLabel)
+ assertThat(info.extendedInfo).isEqualTo(extendedInfo)
+ assertThat(info.displayIconHolder).isEqualTo(displayIconHolder)
+ assertThat(info.allSourceIntents).containsExactly(
+ resolvedIntent, sourceIntent1, sourceIntent2)
+ assertThat(info.allDisplayTargets).containsExactly(displayTarget1, displayTarget2)
+ assertThat(info.isSuspended).isTrue()
+ assertThat(info.isPinned).isTrue()
+ assertThat(info.modifiedScore).isEqualTo(42.0f)
+ assertThat(info.directShareShortcutInfo).isEqualTo(directShareShortcutInfo)
+ assertThat(info.directShareAppTarget).isEqualTo(directShareAppTarget)
+ assertThat(info.displayResolveInfo).isEqualTo(displayResolveInfo)
+ assertThat(info.isEmptyTargetInfo).isFalse()
+ assertThat(info.isPlaceHolderTargetInfo).isFalse()
+ assertThat(info.isNotSelectableTargetInfo).isFalse()
+ assertThat(info.isSelectableTargetInfo).isFalse()
+ assertThat(info.isChooserTargetInfo).isFalse()
+ assertThat(info.isMultiDisplayResolveInfo).isFalse()
+ assertThat(info.isDisplayResolveInfo).isFalse()
+ assertThat(info.hashProvider).isEqualTo(hashProvider)
+ }
+
+ @Test
+ fun testBaseIntentToSend_defaultsToResolvedIntent() {
+ val info = ImmutableTargetInfo.newBuilder().setResolvedIntent(resolvedIntent).build()
+ assertThat(info.baseIntentToSend.filterEquals(resolvedIntent)).isTrue()
+ }
+
+ @Test
+ fun testBaseIntentToSend_fillsInFromReferrerIntent() {
+ val originalIntent = Intent()
+ originalIntent.setPackage("original")
+
+ val referrerFillInIntent = Intent("REFERRER_FILL_IN")
+ referrerFillInIntent.setPackage("referrer")
+
+ val info = ImmutableTargetInfo.newBuilder()
+ .setResolvedIntent(originalIntent)
+ .setReferrerFillInIntent(referrerFillInIntent)
+ .build()
+
+ assertThat(info.baseIntentToSend.getPackage()).isEqualTo("original") // Only fill if empty.
+ assertThat(info.baseIntentToSend.action).isEqualTo("REFERRER_FILL_IN")
+ }
+
+ @Test
+ fun testBaseIntentToSend_fillsInFromRefinementIntent() {
+ val originalIntent = Intent()
+ originalIntent.putExtra("ORIGINAL", true)
+
+ val refinementIntent = Intent()
+ refinementIntent.putExtra("REFINEMENT", true)
+
+ val originalInfo = ImmutableTargetInfo.newBuilder()
+ .setResolvedIntent(originalIntent)
+ .build()
+ val info = originalInfo.tryToCloneWithAppliedRefinement(refinementIntent)
+
+ assertThat(info.baseIntentToSend.getBooleanExtra("ORIGINAL", false)).isTrue()
+ assertThat(info.baseIntentToSend.getBooleanExtra("REFINEMENT", false)).isTrue()
+ }
+
+ @Test
+ fun testBaseIntentToSend_twoFillInSourcesFavorsRefinementRequest() {
+ val originalIntent = Intent("REFINE_ME")
+ originalIntent.setPackage("original")
+
+ val referrerFillInIntent = Intent("REFERRER_FILL_IN")
+ referrerFillInIntent.setPackage("referrer_pkg")
+ referrerFillInIntent.setType("test/referrer")
+
+ val infoWithReferrerFillIn = ImmutableTargetInfo.newBuilder()
+ .setResolvedIntent(originalIntent)
+ .setReferrerFillInIntent(referrerFillInIntent)
+ .build()
+
+ val refinementIntent = Intent("REFINE_ME")
+ refinementIntent.setPackage("original") // Has to match for refinement.
+
+ val info = infoWithReferrerFillIn.tryToCloneWithAppliedRefinement(refinementIntent)
+
+ assertThat(info.baseIntentToSend.getPackage()).isEqualTo("original") // Set all along.
+ assertThat(info.baseIntentToSend.action).isEqualTo("REFINE_ME") // Refinement wins.
+ assertThat(info.baseIntentToSend.type).isEqualTo("test/referrer") // Left for referrer.
+ }
+
+ @Test
+ fun testBaseIntentToSend_doubleRefinementPreservesReferrerFillInButNotOriginalRefinement() {
+ val originalIntent = Intent("REFINE_ME")
+ val referrerFillInIntent = Intent("REFERRER_FILL_IN")
+ referrerFillInIntent.putExtra("TEST", "REFERRER")
+ val refinementIntent1 = Intent("REFINE_ME")
+ refinementIntent1.putExtra("TEST1", "1")
+ val refinementIntent2 = Intent("REFINE_ME")
+ refinementIntent2.putExtra("TEST2", "2")
+
+ val originalInfo = ImmutableTargetInfo.newBuilder()
+ .setResolvedIntent(originalIntent)
+ .setReferrerFillInIntent(referrerFillInIntent)
+ .build()
+
+ val refined1 = originalInfo.tryToCloneWithAppliedRefinement(refinementIntent1)
+ val refined2 = refined1.tryToCloneWithAppliedRefinement(refinementIntent2) // Cloned clone.
+
+ // Both clones get the same values filled in from the referrer intent.
+ assertThat(refined1.baseIntentToSend.getStringExtra("TEST")).isEqualTo("REFERRER")
+ assertThat(refined2.baseIntentToSend.getStringExtra("TEST")).isEqualTo("REFERRER")
+ // Each clone has the respective value that was set in their own refinement request.
+ assertThat(refined1.baseIntentToSend.getStringExtra("TEST1")).isEqualTo("1")
+ assertThat(refined2.baseIntentToSend.getStringExtra("TEST2")).isEqualTo("2")
+ // The clones don't have the data from each other's refinements, even though the intent
+ // field is empty (thus able to be populated by filling-in).
+ assertThat(refined1.baseIntentToSend.getStringExtra("TEST2")).isNull()
+ assertThat(refined2.baseIntentToSend.getStringExtra("TEST1")).isNull()
+ }
+
+ @Test
+ fun testBaseIntentToSend_refinementToAlternateSourceIntent() {
+ val originalIntent = Intent("DONT_REFINE_ME")
+ originalIntent.putExtra("originalIntent", true)
+ val mismatchedAlternate = Intent("DOESNT_MATCH")
+ mismatchedAlternate.putExtra("mismatchedAlternate", true)
+ val targetAlternate = Intent("REFINE_ME")
+ targetAlternate.putExtra("targetAlternate", true)
+ val extraMatch = Intent("REFINE_ME")
+ extraMatch.putExtra("extraMatch", true)
+
+ val originalInfo = ImmutableTargetInfo.newBuilder()
+ .setResolvedIntent(originalIntent)
+ .setAllSourceIntents(listOf(
+ originalIntent, mismatchedAlternate, targetAlternate, extraMatch))
+ .build()
+
+ val refinement = Intent("REFINE_ME") // First match is `targetAlternate`
+ refinement.putExtra("refinement", true)
+
+ val refinedResult = originalInfo.tryToCloneWithAppliedRefinement(refinement)
+ assertThat(refinedResult.baseIntentToSend.getBooleanExtra("refinement", false)).isTrue()
+ assertThat(refinedResult.baseIntentToSend.getBooleanExtra("targetAlternate", false))
+ .isTrue()
+ // None of the other source intents got merged in (not even the later one that matched):
+ assertThat(refinedResult.baseIntentToSend.getBooleanExtra("originalIntent", false))
+ .isFalse()
+ assertThat(refinedResult.baseIntentToSend.getBooleanExtra("mismatchedAlternate", false))
+ .isFalse()
+ assertThat(refinedResult.baseIntentToSend.getBooleanExtra("extraMatch", false)).isFalse()
+ }
+
+ @Test
+ fun testBaseIntentToSend_noSourceIntentMatchingProposedRefinement() {
+ val originalIntent = Intent("DONT_REFINE_ME")
+ originalIntent.putExtra("originalIntent", true)
+ val mismatchedAlternate = Intent("DOESNT_MATCH")
+ mismatchedAlternate.putExtra("mismatchedAlternate", true)
+
+ val originalInfo = ImmutableTargetInfo.newBuilder()
+ .setResolvedIntent(originalIntent)
+ .setAllSourceIntents(listOf(originalIntent, mismatchedAlternate))
+ .build()
+
+ val refinement = Intent("PROPOSED_REFINEMENT")
+ assertThat(originalInfo.tryToCloneWithAppliedRefinement(refinement)).isNull()
+ }
+
+ @Test
+ fun testLegacySubclassRelationships_empty() {
+ val info = ImmutableTargetInfo.newBuilder()
+ .setLegacyType(ImmutableTargetInfo.LegacyTargetType.EMPTY_TARGET_INFO)
+ .build()
+
+ assertThat(info.isEmptyTargetInfo).isTrue()
+ assertThat(info.isPlaceHolderTargetInfo).isFalse()
+ assertThat(info.isNotSelectableTargetInfo).isTrue()
+ assertThat(info.isSelectableTargetInfo).isFalse()
+ assertThat(info.isChooserTargetInfo).isTrue()
+ assertThat(info.isMultiDisplayResolveInfo).isFalse()
+ assertThat(info.isDisplayResolveInfo).isFalse()
+ }
+
+ @Test
+ fun testLegacySubclassRelationships_placeholder() {
+ val info = ImmutableTargetInfo.newBuilder()
+ .setLegacyType(ImmutableTargetInfo.LegacyTargetType.PLACEHOLDER_TARGET_INFO)
+ .build()
+
+ assertThat(info.isEmptyTargetInfo).isFalse()
+ assertThat(info.isPlaceHolderTargetInfo).isTrue()
+ assertThat(info.isNotSelectableTargetInfo).isTrue()
+ assertThat(info.isSelectableTargetInfo).isFalse()
+ assertThat(info.isChooserTargetInfo).isTrue()
+ assertThat(info.isMultiDisplayResolveInfo).isFalse()
+ assertThat(info.isDisplayResolveInfo).isFalse()
+ }
+
+ @Test
+ fun testLegacySubclassRelationships_selectable() {
+ val info = ImmutableTargetInfo.newBuilder()
+ .setLegacyType(ImmutableTargetInfo.LegacyTargetType.SELECTABLE_TARGET_INFO)
+ .build()
+
+ assertThat(info.isEmptyTargetInfo).isFalse()
+ assertThat(info.isPlaceHolderTargetInfo).isFalse()
+ assertThat(info.isNotSelectableTargetInfo).isFalse()
+ assertThat(info.isSelectableTargetInfo).isTrue()
+ assertThat(info.isChooserTargetInfo).isTrue()
+ assertThat(info.isMultiDisplayResolveInfo).isFalse()
+ assertThat(info.isDisplayResolveInfo).isFalse()
+ }
+
+ @Test
+ fun testLegacySubclassRelationships_displayResolveInfo() {
+ val info = ImmutableTargetInfo.newBuilder()
+ .setLegacyType(ImmutableTargetInfo.LegacyTargetType.DISPLAY_RESOLVE_INFO)
+ .build()
+
+ assertThat(info.isEmptyTargetInfo).isFalse()
+ assertThat(info.isPlaceHolderTargetInfo).isFalse()
+ assertThat(info.isNotSelectableTargetInfo).isFalse()
+ assertThat(info.isSelectableTargetInfo).isFalse()
+ assertThat(info.isChooserTargetInfo).isFalse()
+ assertThat(info.isMultiDisplayResolveInfo).isFalse()
+ assertThat(info.isDisplayResolveInfo).isTrue()
+ }
+
+ @Test
+ fun testLegacySubclassRelationships_multiDisplayResolveInfo() {
+ val info = ImmutableTargetInfo.newBuilder()
+ .setLegacyType(ImmutableTargetInfo.LegacyTargetType.MULTI_DISPLAY_RESOLVE_INFO)
+ .build()
+
+ assertThat(info.isEmptyTargetInfo).isFalse()
+ assertThat(info.isPlaceHolderTargetInfo).isFalse()
+ assertThat(info.isNotSelectableTargetInfo).isFalse()
+ assertThat(info.isSelectableTargetInfo).isFalse()
+ assertThat(info.isChooserTargetInfo).isFalse()
+ assertThat(info.isMultiDisplayResolveInfo).isTrue()
+ assertThat(info.isDisplayResolveInfo).isTrue()
+ }
+
+ @Test
+ fun testActivityStarter_correctNumberOfInvocations_startAsCaller() {
+ val activityStarter = object : TestActivityStarter() {
+ override fun startAsUser(
+ target: TargetInfo, activity: Activity, options: Bundle, user: UserHandle
+ ): Boolean {
+ throw RuntimeException("Wrong API used: startAsUser")
+ }
+ }
+
+ val info = ImmutableTargetInfo.newBuilder().setActivityStarter(activityStarter).build()
+ val activity: ResolverActivity = mock()
+ val options = Bundle()
+ options.putInt("TEST_KEY", 1)
+
+ info.startAsCaller(activity, options, 42)
+
+ assertThat(activityStarter.totalInvocations).isEqualTo(1)
+ assertThat(activityStarter.lastInvocationTargetInfo).isEqualTo(info)
+ assertThat(activityStarter.lastInvocationActivity).isEqualTo(activity)
+ assertThat(activityStarter.lastInvocationOptions).isEqualTo(options)
+ assertThat(activityStarter.lastInvocationUserId).isEqualTo(42)
+ assertThat(activityStarter.lastInvocationAsCaller).isTrue()
+ }
+
+ @Test
+ fun testActivityStarter_correctNumberOfInvocations_startAsUser() {
+ val activityStarter = object : TestActivityStarter() {
+ override fun startAsCaller(
+ target: TargetInfo, activity: Activity, options: Bundle, userId: Int): Boolean {
+ throw RuntimeException("Wrong API used: startAsCaller")
+ }
+ }
+
+ val info = ImmutableTargetInfo.newBuilder().setActivityStarter(activityStarter).build()
+ val activity: Activity = mock()
+ val options = Bundle()
+ options.putInt("TEST_KEY", 1)
+
+ info.startAsUser(activity, options, UserHandle.of(42))
+
+ assertThat(activityStarter.totalInvocations).isEqualTo(1)
+ assertThat(activityStarter.lastInvocationTargetInfo).isEqualTo(info)
+ assertThat(activityStarter.lastInvocationActivity).isEqualTo(activity)
+ assertThat(activityStarter.lastInvocationOptions).isEqualTo(options)
+ assertThat(activityStarter.lastInvocationUserId).isEqualTo(42)
+ assertThat(activityStarter.lastInvocationAsCaller).isFalse()
+ }
+
+ @Test
+ fun testActivityStarter_invokedWithRespectiveTargetInfoAfterCopy() {
+ val activityStarter = TestActivityStarter()
+ val info1 = ImmutableTargetInfo.newBuilder().setActivityStarter(activityStarter).build()
+ val info2 = info1.toBuilder().build()
+
+ info1.startAsCaller(mock(), Bundle(), 42)
+ assertThat(activityStarter.lastInvocationTargetInfo).isEqualTo(info1)
+ info2.startAsCaller(mock(), Bundle(), 42)
+ assertThat(activityStarter.lastInvocationTargetInfo).isEqualTo(info2)
+ info2.startAsUser(mock(), Bundle(), UserHandle.of(42))
+ assertThat(activityStarter.lastInvocationTargetInfo).isEqualTo(info2)
+
+ assertThat(activityStarter.totalInvocations).isEqualTo(3) // Instance is still shared.
+ }
+}
+
+private open class TestActivityStarter : ImmutableTargetInfo.TargetActivityStarter {
+ var totalInvocations = 0
+ var lastInvocationTargetInfo: TargetInfo? = null
+ var lastInvocationActivity: Activity? = null
+ var lastInvocationOptions: Bundle? = null
+ var lastInvocationUserId: Integer? = null
+ var lastInvocationAsCaller = false
+
+ override fun startAsCaller(
+ target: TargetInfo, activity: Activity, options: Bundle, userId: Int): Boolean {
+ ++totalInvocations
+ lastInvocationTargetInfo = target
+ lastInvocationActivity = activity
+ lastInvocationOptions = options
+ lastInvocationUserId = Integer(userId)
+ lastInvocationAsCaller = true
+ return true
+ }
+
+ override fun startAsUser(
+ target: TargetInfo, activity: Activity, options: Bundle, user: UserHandle): Boolean {
+ ++totalInvocations
+ lastInvocationTargetInfo = target
+ lastInvocationActivity = activity
+ lastInvocationOptions = options
+ lastInvocationUserId = Integer(user.identifier)
+ lastInvocationAsCaller = false
+ return true
+ }
+}
diff --git a/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt b/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt
index 7c2b07a9..dddbcccb 100644
--- a/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt
+++ b/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt
@@ -22,18 +22,36 @@ import android.content.ComponentName
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.ResolveInfo
+import android.graphics.drawable.AnimatedVectorDrawable
import android.os.UserHandle
+import android.test.UiThreadTest
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.intentresolver.ResolverDataProvider
import com.android.intentresolver.createChooserTarget
import com.android.intentresolver.createShortcutInfo
import com.android.intentresolver.mock
-import com.android.intentresolver.ResolverDataProvider
+import com.android.intentresolver.whenever
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
+import org.mockito.Mockito.any
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
class TargetInfoTest {
private val context = InstrumentationRegistry.getInstrumentation().getContext()
+ @Before
+ fun setup() {
+ // SelectableTargetInfo reads DeviceConfig and needs a permission for that.
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity("android.permission.READ_DEVICE_CONFIG")
+ }
+
@Test
fun testNewEmptyTargetInfo() {
val info = NotSelectableTargetInfo.newEmptyTargetInfo()
@@ -43,13 +61,19 @@ class TargetInfoTest {
assertThat(info.getDisplayIconHolder().getDisplayIcon()).isNull()
}
+ @UiThreadTest // AnimatedVectorDrawable needs to start from a thread with a Looper.
@Test
fun testNewPlaceholderTargetInfo() {
val info = NotSelectableTargetInfo.newPlaceHolderTargetInfo(context)
- assertThat(info.isPlaceHolderTargetInfo()).isTrue()
- assertThat(info.isChooserTargetInfo()).isTrue() // From legacy inheritance model.
+ assertThat(info.isPlaceHolderTargetInfo).isTrue()
+ assertThat(info.isChooserTargetInfo).isTrue() // From legacy inheritance model.
assertThat(info.hasDisplayIcon()).isTrue()
- // TODO: test infrastructure isn't set up to assert anything about the icon itself.
+ assertThat(info.displayIconHolder.displayIcon)
+ .isInstanceOf(AnimatedVectorDrawable::class.java)
+ // TODO: assert that the animation is pre-started/running (IIUC this requires synchronizing
+ // with some "render thread" per the `AnimatedVectorDrawable` docs). I believe this is
+ // possible using `AnimatorTestRule` but I couldn't find any sample usage in Kotlin nor get
+ // it working myself.
}
@Test
@@ -125,6 +149,42 @@ class TargetInfoTest {
}
@Test
+ fun testSelectableTargetInfo_noSourceIntentMatchingProposedRefinement() {
+ val resolvedIntent = Intent("DONT_REFINE_ME")
+ resolvedIntent.putExtra("resolvedIntent", true)
+
+ val baseDisplayInfo = DisplayResolveInfo.newDisplayResolveInfo(
+ resolvedIntent,
+ ResolverDataProvider.createResolveInfo(1, 0),
+ "label",
+ "extended info",
+ resolvedIntent,
+ /* resolveInfoPresentationGetter= */ null)
+ val chooserTarget = createChooserTarget(
+ "title", 0.3f, ResolverDataProvider.createComponentName(2), "test_shortcut_id")
+ val shortcutInfo = createShortcutInfo("id", ResolverDataProvider.createComponentName(3), 3)
+ val appTarget = AppTarget(
+ AppTargetId("id"),
+ chooserTarget.componentName.packageName,
+ chooserTarget.componentName.className,
+ UserHandle.CURRENT)
+
+ val targetInfo = SelectableTargetInfo.newSelectableTargetInfo(
+ baseDisplayInfo,
+ mock(),
+ resolvedIntent,
+ chooserTarget,
+ 0.1f,
+ shortcutInfo,
+ appTarget,
+ mock(),
+ )
+
+ val refinement = Intent("PROPOSED_REFINEMENT")
+ assertThat(targetInfo.tryToCloneWithAppliedRefinement(refinement)).isNull()
+ }
+
+ @Test
fun testNewDisplayResolveInfo() {
val intent = Intent(Intent.ACTION_SEND)
intent.putExtra(Intent.EXTRA_TEXT, "testing intent sending")
@@ -145,6 +205,64 @@ class TargetInfoTest {
}
@Test
+ fun test_DisplayResolveInfo_refinementToAlternateSourceIntent() {
+ val originalIntent = Intent("DONT_REFINE_ME")
+ originalIntent.putExtra("originalIntent", true)
+ val mismatchedAlternate = Intent("DOESNT_MATCH")
+ mismatchedAlternate.putExtra("mismatchedAlternate", true)
+ val targetAlternate = Intent("REFINE_ME")
+ targetAlternate.putExtra("targetAlternate", true)
+ val extraMatch = Intent("REFINE_ME")
+ extraMatch.putExtra("extraMatch", true)
+
+ val originalInfo = DisplayResolveInfo.newDisplayResolveInfo(
+ originalIntent,
+ ResolverDataProvider.createResolveInfo(3, 0),
+ "label",
+ "extended info",
+ originalIntent,
+ /* resolveInfoPresentationGetter= */ null)
+ originalInfo.addAlternateSourceIntent(mismatchedAlternate)
+ originalInfo.addAlternateSourceIntent(targetAlternate)
+ originalInfo.addAlternateSourceIntent(extraMatch)
+
+ val refinement = Intent("REFINE_ME") // First match is `targetAlternate`
+ refinement.putExtra("refinement", true)
+
+ val refinedResult = originalInfo.tryToCloneWithAppliedRefinement(refinement)
+ // Note `DisplayResolveInfo` targets merge refinements directly into their `resolvedIntent`.
+ assertThat(refinedResult.resolvedIntent.getBooleanExtra("refinement", false)).isTrue()
+ assertThat(refinedResult.resolvedIntent.getBooleanExtra("targetAlternate", false))
+ .isTrue()
+ // None of the other source intents got merged in (not even the later one that matched):
+ assertThat(refinedResult.resolvedIntent.getBooleanExtra("originalIntent", false))
+ .isFalse()
+ assertThat(refinedResult.resolvedIntent.getBooleanExtra("mismatchedAlternate", false))
+ .isFalse()
+ assertThat(refinedResult.resolvedIntent.getBooleanExtra("extraMatch", false)).isFalse()
+ }
+
+ @Test
+ fun testDisplayResolveInfo_noSourceIntentMatchingProposedRefinement() {
+ val originalIntent = Intent("DONT_REFINE_ME")
+ originalIntent.putExtra("originalIntent", true)
+ val mismatchedAlternate = Intent("DOESNT_MATCH")
+ mismatchedAlternate.putExtra("mismatchedAlternate", true)
+
+ val originalInfo = DisplayResolveInfo.newDisplayResolveInfo(
+ originalIntent,
+ ResolverDataProvider.createResolveInfo(3, 0),
+ "label",
+ "extended info",
+ originalIntent,
+ /* resolveInfoPresentationGetter= */ null)
+ originalInfo.addAlternateSourceIntent(mismatchedAlternate)
+
+ val refinement = Intent("PROPOSED_REFINEMENT")
+ assertThat(originalInfo.tryToCloneWithAppliedRefinement(refinement)).isNull()
+ }
+
+ @Test
fun testNewMultiDisplayResolveInfo() {
val intent = Intent(Intent.ACTION_SEND)
intent.putExtra(Intent.EXTRA_TEXT, "testing intent sending")
@@ -186,7 +304,93 @@ class TargetInfoTest {
assertThat(multiTargetInfo.hasSelected()).isTrue()
assertThat(multiTargetInfo.getSelectedTarget()).isEqualTo(secondTargetInfo)
+ val refined = multiTargetInfo.tryToCloneWithAppliedRefinement(intent)
+ assertThat(refined).isInstanceOf(MultiDisplayResolveInfo::class.java)
+ assertThat((refined as MultiDisplayResolveInfo).hasSelected())
+ .isEqualTo(multiTargetInfo.hasSelected())
+
// TODO: consider exercising activity-start behavior.
// TODO: consider exercising DisplayResolveInfo base class behavior.
}
+
+ @Test
+ fun testNewMultiDisplayResolveInfo_getAllSourceIntents_fromSelectedTarget() {
+ val sendImage = Intent("SEND").apply { type = "image/png" }
+ val sendUri = Intent("SEND").apply { type = "text/uri" }
+
+ val resolveInfo = ResolverDataProvider.createResolveInfo(1, 0)
+
+ val imageOnlyTarget = DisplayResolveInfo.newDisplayResolveInfo(
+ sendImage,
+ resolveInfo,
+ "Send Image",
+ "Sends only images",
+ sendImage,
+ /* resolveInfoPresentationGetter= */ null)
+
+ val textOnlyTarget = DisplayResolveInfo.newDisplayResolveInfo(
+ sendUri,
+ resolveInfo,
+ "Send Text",
+ "Sends only text",
+ sendUri,
+ /* resolveInfoPresentationGetter= */ null)
+
+ val imageOrTextTarget = DisplayResolveInfo.newDisplayResolveInfo(
+ sendImage,
+ resolveInfo,
+ "Send Image or Text",
+ "Sends images or text",
+ sendImage,
+ /* resolveInfoPresentationGetter= */ null
+ ).apply {
+ addAlternateSourceIntent(sendUri)
+ }
+
+ val multiTargetInfo = MultiDisplayResolveInfo.newMultiDisplayResolveInfo(
+ listOf(imageOnlyTarget, textOnlyTarget, imageOrTextTarget)
+ )
+
+ multiTargetInfo.setSelected(0)
+ assertThat(multiTargetInfo.selectedTarget).isEqualTo(imageOnlyTarget)
+ assertThat(multiTargetInfo.allSourceIntents).isEqualTo(imageOnlyTarget.allSourceIntents)
+
+ multiTargetInfo.setSelected(1)
+ assertThat(multiTargetInfo.selectedTarget).isEqualTo(textOnlyTarget)
+ assertThat(multiTargetInfo.allSourceIntents).isEqualTo(textOnlyTarget.allSourceIntents)
+
+ multiTargetInfo.setSelected(2)
+ assertThat(multiTargetInfo.selectedTarget).isEqualTo(imageOrTextTarget)
+ assertThat(multiTargetInfo.allSourceIntents).isEqualTo(imageOrTextTarget.allSourceIntents)
+ }
+
+ @Test
+ fun testNewMultiDisplayResolveInfo_tryToCloneWithAppliedRefinement_delegatedToSelectedTarget() {
+ val refined = Intent("SEND")
+ val sendImage = Intent("SEND")
+ val targetOne = spy(
+ DisplayResolveInfo.newDisplayResolveInfo(
+ sendImage,
+ ResolverDataProvider.createResolveInfo(1, 0),
+ "Target One",
+ "Target One",
+ sendImage,
+ /* resolveInfoPresentationGetter= */ null
+ )
+ )
+ val targetTwo = mock<DisplayResolveInfo> {
+ whenever(tryToCloneWithAppliedRefinement(any())).thenReturn(this)
+ }
+
+ val multiTargetInfo = MultiDisplayResolveInfo.newMultiDisplayResolveInfo(
+ listOf(targetOne, targetTwo)
+ )
+
+ multiTargetInfo.setSelected(1)
+ assertThat(multiTargetInfo.selectedTarget).isEqualTo(targetTwo)
+
+ multiTargetInfo.tryToCloneWithAppliedRefinement(refined)
+ verify(targetTwo, times(1)).tryToCloneWithAppliedRefinement(refined)
+ verify(targetOne, never()).tryToCloneWithAppliedRefinement(any())
+ }
}
diff --git a/java/tests/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt b/java/tests/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
new file mode 100644
index 00000000..d870a8c2
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.intentresolver.contentpreview
+
+import android.content.ClipDescription
+import android.content.ContentInterface
+import android.content.Intent
+import android.graphics.Bitmap
+import android.net.Uri
+import com.android.intentresolver.ImageLoader
+import com.android.intentresolver.TestFeatureFlagRepository
+import com.android.intentresolver.contentpreview.ChooserContentPreviewUi.ActionFactory
+import com.android.intentresolver.flags.Flags
+import com.android.intentresolver.mock
+import com.android.intentresolver.whenever
+import com.android.intentresolver.widget.ActionRow
+import com.android.intentresolver.widget.ImagePreviewView
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import java.util.function.Consumer
+
+private const val PROVIDER_NAME = "org.pkg.app"
+class ChooserContentPreviewUiTest {
+ private val contentResolver = mock<ContentInterface>()
+ private val imageClassifier = ChooserContentPreviewUi.ImageMimeTypeClassifier { mimeType ->
+ mimeType != null && ClipDescription.compareMimeTypes(mimeType, "image/*")
+ }
+ private val imageLoader = object : ImageLoader {
+ override fun loadImage(uri: Uri, callback: Consumer<Bitmap?>) {
+ callback.accept(null)
+ }
+ override fun prePopulate(uris: List<Uri>) = Unit
+ override suspend fun invoke(uri: Uri): Bitmap? = null
+ }
+ private val actionFactory = object : ActionFactory {
+ override fun createCopyButton() = ActionRow.Action(label = "Copy", icon = null) {}
+ override fun createEditButton(): ActionRow.Action? = null
+ override fun createNearbyButton(): ActionRow.Action? = null
+ override fun createCustomActions(): List<ActionRow.Action> = emptyList()
+ override fun getModifyShareAction(): Runnable? = null
+ override fun getExcludeSharedTextAction(): Consumer<Boolean> = Consumer<Boolean> {}
+ }
+ private val transitionCallback = mock<ImagePreviewView.TransitionElementStatusCallback>()
+ private val featureFlagRepository = TestFeatureFlagRepository(
+ mapOf(
+ Flags.SHARESHEET_SCROLLABLE_IMAGE_PREVIEW to true
+ )
+ )
+
+ @Test
+ fun test_ChooserContentPreview_non_send_intent_action_to_text_preview() {
+ val targetIntent = Intent(Intent.ACTION_VIEW)
+ val testSubject = ChooserContentPreviewUi(
+ targetIntent,
+ contentResolver,
+ imageClassifier,
+ imageLoader,
+ actionFactory,
+ transitionCallback,
+ featureFlagRepository
+ )
+ assertThat(testSubject.preferredContentPreview)
+ .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT)
+ verify(transitionCallback, times(1)).onAllTransitionElementsReady()
+ }
+
+ @Test
+ fun test_ChooserContentPreview_text_mime_type_to_text_preview() {
+ val targetIntent = Intent(Intent.ACTION_SEND).apply {
+ type = "text/plain"
+ putExtra(Intent.EXTRA_TEXT, "Text Extra")
+ }
+ val testSubject = ChooserContentPreviewUi(
+ targetIntent,
+ contentResolver,
+ imageClassifier,
+ imageLoader,
+ actionFactory,
+ transitionCallback,
+ featureFlagRepository
+ )
+ assertThat(testSubject.preferredContentPreview)
+ .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT)
+ verify(transitionCallback, times(1)).onAllTransitionElementsReady()
+ }
+
+ @Test
+ fun test_ChooserContentPreview_single_image_uri_to_image_preview() {
+ val uri = Uri.parse("content://$PROVIDER_NAME/test.png")
+ val targetIntent = Intent(Intent.ACTION_SEND).apply {
+ putExtra(Intent.EXTRA_STREAM, uri)
+ }
+ whenever(contentResolver.getType(uri)).thenReturn("image/png")
+ val testSubject = ChooserContentPreviewUi(
+ targetIntent,
+ contentResolver,
+ imageClassifier,
+ imageLoader,
+ actionFactory,
+ transitionCallback,
+ featureFlagRepository
+ )
+ assertThat(testSubject.preferredContentPreview)
+ .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
+ verify(transitionCallback, never()).onAllTransitionElementsReady()
+ }
+
+ @Test
+ fun test_ChooserContentPreview_single_non_image_uri_to_file_preview() {
+ val uri = Uri.parse("content://$PROVIDER_NAME/test.pdf")
+ val targetIntent = Intent(Intent.ACTION_SEND).apply {
+ putExtra(Intent.EXTRA_STREAM, uri)
+ }
+ whenever(contentResolver.getType(uri)).thenReturn("application/pdf")
+ val testSubject = ChooserContentPreviewUi(
+ targetIntent,
+ contentResolver,
+ imageClassifier,
+ imageLoader,
+ actionFactory,
+ transitionCallback,
+ featureFlagRepository
+ )
+ assertThat(testSubject.preferredContentPreview)
+ .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
+ verify(transitionCallback, times(1)).onAllTransitionElementsReady()
+ }
+
+ @Test
+ fun test_ChooserContentPreview_multiple_image_uri_to_image_preview() {
+ val uri1 = Uri.parse("content://$PROVIDER_NAME/test.png")
+ val uri2 = Uri.parse("content://$PROVIDER_NAME/test.jpg")
+ val targetIntent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
+ putExtra(
+ Intent.EXTRA_STREAM,
+ ArrayList<Uri>().apply {
+ add(uri1)
+ add(uri2)
+ }
+ )
+ }
+ whenever(contentResolver.getType(uri1)).thenReturn("image/png")
+ whenever(contentResolver.getType(uri2)).thenReturn("image/jpeg")
+ val testSubject = ChooserContentPreviewUi(
+ targetIntent,
+ contentResolver,
+ imageClassifier,
+ imageLoader,
+ actionFactory,
+ transitionCallback,
+ featureFlagRepository
+ )
+ assertThat(testSubject.preferredContentPreview)
+ .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
+ verify(transitionCallback, never()).onAllTransitionElementsReady()
+ }
+
+ @Test
+ fun test_ChooserContentPreview_some_non_image_uri_to_file_preview() {
+ val uri1 = Uri.parse("content://$PROVIDER_NAME/test.png")
+ val uri2 = Uri.parse("content://$PROVIDER_NAME/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("image/png")
+ whenever(contentResolver.getType(uri2)).thenReturn("application/pdf")
+ val testSubject = ChooserContentPreviewUi(
+ targetIntent,
+ contentResolver,
+ imageClassifier,
+ imageLoader,
+ actionFactory,
+ transitionCallback,
+ featureFlagRepository
+ )
+ assertThat(testSubject.preferredContentPreview)
+ .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
+ verify(transitionCallback, times(1)).onAllTransitionElementsReady()
+ }
+}
diff --git a/java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java b/java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java
index 448718cd..006f3b2d 100644
--- a/java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java
+++ b/java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java
@@ -27,7 +27,7 @@ import android.os.Message;
import androidx.test.InstrumentationRegistry;
-import com.android.intentresolver.ResolverActivity;
+import com.android.intentresolver.ResolvedComponentInfo;
import org.junit.Test;
@@ -37,12 +37,12 @@ public class AbstractResolverComparatorTest {
@Test
public void testPinned() {
- ResolverActivity.ResolvedComponentInfo r1 = new ResolverActivity.ResolvedComponentInfo(
+ ResolvedComponentInfo r1 = new ResolvedComponentInfo(
new ComponentName("package", "class"), new Intent(), new ResolveInfo()
);
r1.setPinned(true);
- ResolverActivity.ResolvedComponentInfo r2 = new ResolverActivity.ResolvedComponentInfo(
+ ResolvedComponentInfo r2 = new ResolvedComponentInfo(
new ComponentName("zackage", "zlass"), new Intent(), new ResolveInfo()
);
@@ -60,14 +60,14 @@ public class AbstractResolverComparatorTest {
pmInfo1.activityInfo = new ActivityInfo();
pmInfo1.activityInfo.packageName = "aaa";
- ResolverActivity.ResolvedComponentInfo r1 = new ResolverActivity.ResolvedComponentInfo(
+ ResolvedComponentInfo r1 = new ResolvedComponentInfo(
new ComponentName("package", "class"), new Intent(), pmInfo1);
r1.setPinned(true);
ResolveInfo pmInfo2 = new ResolveInfo();
pmInfo2.activityInfo = new ActivityInfo();
pmInfo2.activityInfo.packageName = "zzz";
- ResolverActivity.ResolvedComponentInfo r2 = new ResolverActivity.ResolvedComponentInfo(
+ ResolvedComponentInfo r2 = new ResolvedComponentInfo(
new ComponentName("zackage", "zlass"), new Intent(), pmInfo2);
r2.setPinned(true);
@@ -91,7 +91,7 @@ public class AbstractResolverComparatorTest {
}
@Override
- void doCompute(List<ResolverActivity.ResolvedComponentInfo> targets) {}
+ void doCompute(List<ResolvedComponentInfo> targets) {}
@Override
public float getScore(ComponentName name) {
diff --git a/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt b/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt
index 5756a0cd..0c817cb2 100644
--- a/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt
+++ b/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt
@@ -28,6 +28,8 @@ import android.os.UserHandle
import android.os.UserManager
import androidx.test.filters.SmallTest
import com.android.intentresolver.any
+import com.android.intentresolver.argumentCaptor
+import com.android.intentresolver.capture
import com.android.intentresolver.chooser.DisplayResolveInfo
import com.android.intentresolver.createAppTarget
import com.android.intentresolver.createShareShortcutInfo
@@ -39,8 +41,8 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
-import org.mockito.ArgumentCaptor
import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -56,9 +58,15 @@ class ShortcutLoaderTest {
private val pm = mock<PackageManager> {
whenever(getApplicationInfo(any(), any<ApplicationInfoFlags>())).thenReturn(appInfo)
}
+ val userManager = mock<UserManager> {
+ whenever(isUserRunning(any<UserHandle>())).thenReturn(true)
+ whenever(isUserUnlocked(any<UserHandle>())).thenReturn(true)
+ whenever(isQuietModeEnabled(any<UserHandle>())).thenReturn(false)
+ }
private val context = mock<Context> {
whenever(packageManager).thenReturn(pm)
whenever(createContextAsUser(any(), anyInt())).thenReturn(this)
+ whenever(getSystemService(Context.USER_SERVICE)).thenReturn(userManager)
}
private val executor = ImmediateExecutor()
private val intentFilter = mock<IntentFilter>()
@@ -66,7 +74,7 @@ class ShortcutLoaderTest {
private val callback = mock<Consumer<ShortcutLoader.Result>>()
@Test
- fun test_app_predictor_result() {
+ fun test_queryShortcuts_result_consistency_with_AppPredictor() {
val componentName = ComponentName("pkg", "Class")
val appTarget = mock<DisplayResolveInfo> {
whenever(resolvedComponentName).thenReturn(componentName)
@@ -85,24 +93,22 @@ class ShortcutLoaderTest {
testSubject.queryShortcuts(appTargets)
- verify(appPredictor, times(1)).requestPredictionUpdate()
- val appPredictorCallbackCaptor = ArgumentCaptor.forClass(AppPredictor.Callback::class.java)
- verify(appPredictor, times(1))
- .registerPredictionUpdates(any(), appPredictorCallbackCaptor.capture())
-
val matchingShortcutInfo = createShortcutInfo("id-0", componentName, 1)
val matchingAppTarget = createAppTarget(matchingShortcutInfo)
val shortcuts = listOf(
matchingAppTarget,
- // mismatching shortcut
+ // 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(), capture(appPredictorCallbackCaptor))
appPredictorCallbackCaptor.value.onTargetsAvailable(shortcuts)
- val resultCaptor = ArgumentCaptor.forClass(ShortcutLoader.Result::class.java)
- verify(callback, times(1)).accept(resultCaptor.capture())
+ val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
+ verify(callback, times(1)).accept(capture(resultCaptor))
val result = resultCaptor.value
assertTrue("An app predictor result is expected", result.isFromAppPredictor)
@@ -124,7 +130,7 @@ class ShortcutLoaderTest {
}
@Test
- fun test_shortcut_manager_result() {
+ fun test_queryShortcuts_result_consistency_with_ShortcutManager() {
val componentName = ComponentName("pkg", "Class")
val appTarget = mock<DisplayResolveInfo> {
whenever(resolvedComponentName).thenReturn(componentName)
@@ -153,8 +159,8 @@ class ShortcutLoaderTest {
testSubject.queryShortcuts(appTargets)
- val resultCaptor = ArgumentCaptor.forClass(ShortcutLoader.Result::class.java)
- verify(callback, times(1)).accept(resultCaptor.capture())
+ val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
+ verify(callback, times(1)).accept(capture(resultCaptor))
val result = resultCaptor.value
assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor)
@@ -175,7 +181,7 @@ class ShortcutLoaderTest {
}
@Test
- fun test_fallback_to_shortcut_manager() {
+ fun test_queryShortcuts_falls_back_to_ShortcutManager_on_empty_reply() {
val componentName = ComponentName("pkg", "Class")
val appTarget = mock<DisplayResolveInfo> {
whenever(resolvedComponentName).thenReturn(componentName)
@@ -205,13 +211,13 @@ class ShortcutLoaderTest {
testSubject.queryShortcuts(appTargets)
verify(appPredictor, times(1)).requestPredictionUpdate()
- val appPredictorCallbackCaptor = ArgumentCaptor.forClass(AppPredictor.Callback::class.java)
+ val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>()
verify(appPredictor, times(1))
- .registerPredictionUpdates(any(), appPredictorCallbackCaptor.capture())
+ .registerPredictionUpdates(any(), capture(appPredictorCallbackCaptor))
appPredictorCallbackCaptor.value.onTargetsAvailable(emptyList())
- val resultCaptor = ArgumentCaptor.forClass(ShortcutLoader.Result::class.java)
- verify(callback, times(1)).accept(resultCaptor.capture())
+ val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
+ verify(callback, times(1)).accept(capture(resultCaptor))
val result = resultCaptor.value
assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor)
@@ -232,32 +238,32 @@ class ShortcutLoaderTest {
}
@Test
- fun test_do_not_call_services_for_not_running_work_profile() {
+ fun test_queryShortcuts_do_not_call_services_for_not_running_work_profile() {
testDisabledWorkProfileDoNotCallSystem(isUserRunning = false)
}
@Test
- fun test_do_not_call_services_for_locked_work_profile() {
+ fun test_queryShortcuts_do_not_call_services_for_locked_work_profile() {
testDisabledWorkProfileDoNotCallSystem(isUserUnlocked = false)
}
@Test
- fun test_do_not_call_services_if_quite_mode_is_enabled_for_work_profile() {
+ fun test_queryShortcuts_do_not_call_services_if_quite_mode_is_enabled_for_work_profile() {
testDisabledWorkProfileDoNotCallSystem(isQuietModeEnabled = true)
}
@Test
- fun test_call_services_for_not_running_main_profile() {
+ fun test_queryShortcuts_call_services_for_not_running_main_profile() {
testAlwaysCallSystemForMainProfile(isUserRunning = false)
}
@Test
- fun test_call_services_for_locked_main_profile() {
+ fun test_queryShortcuts_call_services_for_locked_main_profile() {
testAlwaysCallSystemForMainProfile(isUserUnlocked = false)
}
@Test
- fun test_call_services_if_quite_mode_is_enabled_for_main_profile() {
+ fun test_queryShortcuts_call_services_if_quite_mode_is_enabled_for_main_profile() {
testAlwaysCallSystemForMainProfile(isQuietModeEnabled = true)
}
@@ -267,7 +273,7 @@ class ShortcutLoaderTest {
isQuietModeEnabled: Boolean = false
) {
val userHandle = UserHandle.of(10)
- val userManager = mock<UserManager> {
+ with(userManager) {
whenever(isUserRunning(userHandle)).thenReturn(isUserRunning)
whenever(isUserUnlocked(userHandle)).thenReturn(isUserUnlocked)
whenever(isQuietModeEnabled(userHandle)).thenReturn(isQuietModeEnabled)
@@ -297,7 +303,7 @@ class ShortcutLoaderTest {
isQuietModeEnabled: Boolean = false
) {
val userHandle = UserHandle.of(10)
- val userManager = mock<UserManager> {
+ with(userManager) {
whenever(isUserRunning(userHandle)).thenReturn(isUserRunning)
whenever(isUserUnlocked(userHandle)).thenReturn(isUserUnlocked)
whenever(isQuietModeEnabled(userHandle)).thenReturn(isQuietModeEnabled)