summaryrefslogtreecommitdiff
path: root/java/tests
diff options
context:
space:
mode:
author Mark Renouf <mrenouf@google.com> 2023-11-21 13:31:12 -0500
committer Mark Renouf <mrenouf@google.com> 2023-11-28 15:40:20 -0500
commitc5d116c2977f5ed83f6994130a3b180deb33f266 (patch)
tree29a9481348e00c98eaaa60fba4bb9c0c97bf0fde /java/tests
parentc1c24280a0dfe39745704398ce8d13cf14404eb2 (diff)
Splits tests
This splits the monolithic 'tests' target into multiple types based on classification. See the corresponding README.md for descriptions. The target 'IntentResolverUnitTests' is replaced by: IntentResolver-tests-unit IntentResolver-tests-activity IntentResolver-tests-integration IntentResolver-tests-shared To run everything, use: atest IntentResolver-tests-* To run TreeHugger presubmits: atest --test-mapping pacakges/modules/IntentResolver Equivalently for postsubmit: atest --test-mapping pacakges/modules/IntentResolver:postsubmit Bug: 300157408 Test: atest IntentResolver-tests-* Change-Id: I9d499284f070a4bfa3e7c1b7c3bbfaa8adb3379b
Diffstat (limited to 'java/tests')
-rw-r--r--java/tests/Android.bp67
-rw-r--r--java/tests/AndroidManifest.xml42
-rw-r--r--java/tests/AndroidTest.xml28
-rw-r--r--java/tests/README.md7
-rw-r--r--java/tests/res/drawable/test320x240.pngbin39533 -> 0 bytes
-rw-r--r--java/tests/src/com/android/intentresolver/AnnotatedUserHandlesTest.kt79
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt227
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java129
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserIntegratedDeviceComponentsTest.kt71
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserListAdapterDataTest.kt179
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt225
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt242
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserRequestParametersTest.kt87
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java274
-rw-r--r--java/tests/src/com/android/intentresolver/EnterTransitionAnimationDelegateTest.kt112
-rw-r--r--java/tests/src/com/android/intentresolver/FakeResolverListCommunicator.kt56
-rw-r--r--java/tests/src/com/android/intentresolver/IChooserWrapper.java49
-rw-r--r--java/tests/src/com/android/intentresolver/MatcherUtils.java51
-rw-r--r--java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt177
-rw-r--r--java/tests/src/com/android/intentresolver/MultiProfilePagerAdapterTest.kt277
-rw-r--r--java/tests/src/com/android/intentresolver/ResolverActivityTest.java1097
-rw-r--r--java/tests/src/com/android/intentresolver/ResolverDataProvider.java257
-rw-r--r--java/tests/src/com/android/intentresolver/ResolverListAdapterTest.kt1048
-rw-r--r--java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java285
-rw-r--r--java/tests/src/com/android/intentresolver/ShortcutSelectionLogicTest.kt312
-rw-r--r--java/tests/src/com/android/intentresolver/TargetPresentationGetterTest.kt204
-rw-r--r--java/tests/src/com/android/intentresolver/TestContentPreviewViewModel.kt56
-rw-r--r--java/tests/src/com/android/intentresolver/TestContentProvider.kt69
-rw-r--r--java/tests/src/com/android/intentresolver/TestHelpers.kt71
-rw-r--r--java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt33
-rw-r--r--java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java3127
-rw-r--r--java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java480
-rw-r--r--java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt502
-rw-r--r--java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt397
-rw-r--r--java/tests/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt139
-rw-r--r--java/tests/src/com/android/intentresolver/contentpreview/ContentPreviewUiTest.kt41
-rw-r--r--java/tests/src/com/android/intentresolver/contentpreview/FileContentPreviewUiTest.kt99
-rw-r--r--java/tests/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUiTest.kt423
-rw-r--r--java/tests/src/com/android/intentresolver/contentpreview/HeadlineGeneratorImplTest.kt61
-rw-r--r--java/tests/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt367
-rw-r--r--java/tests/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt367
-rw-r--r--java/tests/src/com/android/intentresolver/contentpreview/TextContentPreviewUiTest.kt108
-rw-r--r--java/tests/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUiTest.kt348
-rw-r--r--java/tests/src/com/android/intentresolver/emptystate/CompositeEmptyStateProviderTest.kt65
-rw-r--r--java/tests/src/com/android/intentresolver/emptystate/CrossProfileIntentsCheckerTest.kt84
-rw-r--r--java/tests/src/com/android/intentresolver/emptystate/EmptyStateUiHelperTest.kt113
-rw-r--r--java/tests/src/com/android/intentresolver/logging/EventLogImplTest.java425
-rw-r--r--java/tests/src/com/android/intentresolver/logging/FakeEventLog.kt197
-rw-r--r--java/tests/src/com/android/intentresolver/logging/FakeFrameworkStatsLogger.kt95
-rw-r--r--java/tests/src/com/android/intentresolver/logging/TestEventLogModule.kt39
-rw-r--r--java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java141
-rw-r--r--java/tests/src/com/android/intentresolver/shortcuts/ScopedAppTargetListCallbackTest.kt71
-rw-r--r--java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt490
-rw-r--r--java/tests/src/com/android/intentresolver/shortcuts/ShortcutToChooserTargetConverterTest.kt177
-rw-r--r--java/tests/src/com/android/intentresolver/util/TestExecutor.kt40
-rw-r--r--java/tests/src/com/android/intentresolver/util/UriFiltersTest.kt95
-rw-r--r--java/tests/src/com/android/intentresolver/v2/ChooserActionFactoryTest.kt232
-rw-r--r--java/tests/src/com/android/intentresolver/v2/ChooserActivityOverrideData.java131
-rw-r--r--java/tests/src/com/android/intentresolver/v2/ChooserWrapperActivity.java265
-rw-r--r--java/tests/src/com/android/intentresolver/v2/MultiProfilePagerAdapterTest.kt285
-rw-r--r--java/tests/src/com/android/intentresolver/v2/ResolverActivityTest.java1105
-rw-r--r--java/tests/src/com/android/intentresolver/v2/ResolverWrapperActivity.java284
-rw-r--r--java/tests/src/com/android/intentresolver/v2/TestChooserActivityLogic.kt32
-rw-r--r--java/tests/src/com/android/intentresolver/v2/TestResolverActivityLogic.kt22
-rw-r--r--java/tests/src/com/android/intentresolver/v2/UnbundledChooserActivityTest.java3147
-rw-r--r--java/tests/src/com/android/intentresolver/v2/UnbundledChooserActivityWorkProfileTest.java481
-rw-r--r--java/tests/src/com/android/intentresolver/v2/coroutines/Flow.kt89
-rw-r--r--java/tests/src/com/android/intentresolver/v2/data/repository/UserRepositoryImplTest.kt222
-rw-r--r--java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt228
-rw-r--r--java/tests/src/com/android/intentresolver/v2/listcontroller/ChooserRequestFilteredComponentsTest.kt61
-rw-r--r--java/tests/src/com/android/intentresolver/v2/listcontroller/FakeResolverComparator.kt83
-rw-r--r--java/tests/src/com/android/intentresolver/v2/listcontroller/FilterableComponentsTest.kt77
-rw-r--r--java/tests/src/com/android/intentresolver/v2/listcontroller/IntentResolverTest.kt499
-rw-r--r--java/tests/src/com/android/intentresolver/v2/listcontroller/LastChosenManagerTest.kt111
-rw-r--r--java/tests/src/com/android/intentresolver/v2/listcontroller/PinnableComponentsTest.kt74
-rw-r--r--java/tests/src/com/android/intentresolver/v2/listcontroller/ResolveListDeduperTest.kt125
-rw-r--r--java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentFilteringTest.kt309
-rw-r--r--java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentSortingTest.kt197
-rw-r--r--java/tests/src/com/android/intentresolver/v2/listcontroller/SharedPreferencesPinnedComponentsTest.kt63
-rw-r--r--java/tests/src/com/android/intentresolver/v2/platform/FakeSecureSettings.kt44
-rw-r--r--java/tests/src/com/android/intentresolver/v2/platform/FakeSecureSettingsTest.kt61
-rw-r--r--java/tests/src/com/android/intentresolver/v2/platform/FakeUserManager.kt239
-rw-r--r--java/tests/src/com/android/intentresolver/v2/platform/FakeUserManagerTest.kt128
-rw-r--r--java/tests/src/com/android/intentresolver/v2/platform/NearbyShareModuleTest.kt83
-rw-r--r--java/tests/src/com/android/intentresolver/widget/BatchPreviewLoaderTest.kt211
85 files changed, 0 insertions, 23190 deletions
diff --git a/java/tests/Android.bp b/java/tests/Android.bp
deleted file mode 100644
index 75dd2c19..00000000
--- a/java/tests/Android.bp
+++ /dev/null
@@ -1,67 +0,0 @@
-//
-// Copyright (C) 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-android_test {
- name: "IntentResolverUnitTests",
- manifest: "AndroidManifest.xml",
- // Include all test java files.
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
-
- libs: [
- "android.test.runner",
- "android.test.base",
- "android.test.mock",
- "framework",
- "framework-res",
- ],
-
- resource_dirs: ["res"],
- test_config: "AndroidTest.xml",
- static_libs: [
- "androidx.test.core",
- "androidx.test.ext.junit",
- "androidx.test.ext.truth",
- "androidx.test.espresso.contrib",
- "androidx.test.espresso.core",
- "androidx.test.rules",
- "androidx.lifecycle_lifecycle-common-java8",
- "androidx.lifecycle_lifecycle-extensions",
- "androidx.lifecycle_lifecycle-runtime-testing",
- "hilt_android_testing",
- "IntentResolver-core",
- "junit",
- "kotlinx_coroutines_test",
- "mockito-target-minus-junit4",
- "testables",
- "truth",
- "truth-java8-extension",
- "flag-junit",
- "platform-test-annotations",
- ],
- plugins: ["dagger2-compiler"],
- test_suites: ["general-tests"],
- sdk_version: "core_platform",
- min_sdk_version: "current",
- target_sdk_version: "current",
- platform_apis: true,
-}
diff --git a/java/tests/AndroidManifest.xml b/java/tests/AndroidManifest.xml
deleted file mode 100644
index 03e32c65..00000000
--- a/java/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.intentresolver.tests">
-
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
- <uses-permission android:name="android.permission.QUERY_USERS"/>
- <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND"/>
- <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
- <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
-
- <application android:name="dagger.hilt.android.testing.HiltTestApplication">
- <uses-library android:name="android.test.runner" />
- <activity android:name="com.android.intentresolver.ChooserWrapperActivity" />
- <activity android:name="com.android.intentresolver.ResolverWrapperActivity" />
- <activity android:name="com.android.intentresolver.v2.ChooserWrapperActivity" />
- <activity android:name="com.android.intentresolver.v2.ResolverWrapperActivity" />
- <provider
- android:authorities="com.android.intentresolver.tests"
- android:name="com.android.intentresolver.TestContentProvider"
- android:grantUriPermissions="true" />
- </application>
-
- <instrumentation android:name="android.testing.TestableInstrumentation"
- android:targetPackage="com.android.intentresolver.tests">
- </instrumentation>
-
-</manifest>
diff --git a/java/tests/AndroidTest.xml b/java/tests/AndroidTest.xml
deleted file mode 100644
index d1d77c10..00000000
--- a/java/tests/AndroidTest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 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.
--->
-<configuration description="Run IntentResolver Tests.">
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="test-file-name" value="IntentResolverUnitTests.apk" />
- </target_preparer>
-
- <option name="test-suite-tag" value="apct" />
- <option name="test-tag" value="IntentResolverUnitTests" />
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="com.android.intentresolver.tests" />
- <option name="runner" value="android.testing.TestableInstrumentation" />
- <option name="hidden-api-checks" value="false"/>
- </test>
-</configuration>
diff --git a/java/tests/README.md b/java/tests/README.md
deleted file mode 100644
index b42e8d27..00000000
--- a/java/tests/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-
-__ABOUT__
-
-This is package is configured as a self-instrumenting test APK. The IntentResolver application code
-is bundled in and the instrumentation APK targets itself. This is used for in-process mocking and
-verification involving live components that many tests rely on.
-
diff --git a/java/tests/res/drawable/test320x240.png b/java/tests/res/drawable/test320x240.png
deleted file mode 100644
index 9b5800da..00000000
--- a/java/tests/res/drawable/test320x240.png
+++ /dev/null
Binary files differ
diff --git a/java/tests/src/com/android/intentresolver/AnnotatedUserHandlesTest.kt b/java/tests/src/com/android/intentresolver/AnnotatedUserHandlesTest.kt
deleted file mode 100644
index cd2fbc7a..00000000
--- a/java/tests/src/com/android/intentresolver/AnnotatedUserHandlesTest.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver
-
-import android.os.UserHandle
-
-import com.google.common.truth.Truth.assertThat
-
-import org.junit.Test
-
-class AnnotatedUserHandlesTest {
-
- @Test
- fun testBasicProperties() { // Fields that are reflected back w/o logic.
- val info = AnnotatedUserHandles.newBuilder()
- .setUserIdOfCallingApp(42)
- .setUserHandleSharesheetLaunchedAs(UserHandle.of(116))
- .setPersonalProfileUserHandle(UserHandle.of(117))
- .setWorkProfileUserHandle(UserHandle.of(118))
- .setCloneProfileUserHandle(UserHandle.of(119))
- .build()
-
- assertThat(info.userIdOfCallingApp).isEqualTo(42)
- assertThat(info.userHandleSharesheetLaunchedAs.identifier).isEqualTo(116)
- assertThat(info.personalProfileUserHandle.identifier).isEqualTo(117)
- assertThat(info.workProfileUserHandle?.identifier).isEqualTo(118)
- assertThat(info.cloneProfileUserHandle?.identifier).isEqualTo(119)
- }
-
- @Test
- fun testWorkTabInitiallySelectedWhenLaunchedFromWorkProfile() {
- val info = AnnotatedUserHandles.newBuilder()
- .setUserIdOfCallingApp(42)
- .setPersonalProfileUserHandle(UserHandle.of(101))
- .setWorkProfileUserHandle(UserHandle.of(202))
- .setUserHandleSharesheetLaunchedAs(UserHandle.of(202))
- .build()
-
- assertThat(info.tabOwnerUserHandleForLaunch.identifier).isEqualTo(202)
- }
-
- @Test
- fun testPersonalTabInitiallySelectedWhenLaunchedFromPersonalProfile() {
- val info = AnnotatedUserHandles.newBuilder()
- .setUserIdOfCallingApp(42)
- .setPersonalProfileUserHandle(UserHandle.of(101))
- .setWorkProfileUserHandle(UserHandle.of(202))
- .setUserHandleSharesheetLaunchedAs(UserHandle.of(101))
- .build()
-
- assertThat(info.tabOwnerUserHandleForLaunch.identifier).isEqualTo(101)
- }
-
- @Test
- fun testPersonalTabInitiallySelectedWhenLaunchedFromOtherProfile() {
- val info = AnnotatedUserHandles.newBuilder()
- .setUserIdOfCallingApp(42)
- .setPersonalProfileUserHandle(UserHandle.of(101))
- .setWorkProfileUserHandle(UserHandle.of(202))
- .setUserHandleSharesheetLaunchedAs(UserHandle.of(303))
- .build()
-
- assertThat(info.tabOwnerUserHandleForLaunch.identifier).isEqualTo(101)
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt b/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt
deleted file mode 100644
index 55a94ebd..00000000
--- a/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver
-
-import android.app.Activity
-import android.app.PendingIntent
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Context.RECEIVER_EXPORTED
-import android.content.Intent
-import android.content.IntentFilter
-import android.content.res.Resources
-import android.graphics.drawable.Icon
-import android.service.chooser.ChooserAction
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.intentresolver.logging.EventLog
-import com.google.common.collect.ImmutableList
-import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-import java.util.function.Consumer
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito
-
-@RunWith(AndroidJUnit4::class)
-class ChooserActionFactoryTest {
- private val context = InstrumentationRegistry.getInstrumentation().getContext()
-
- private val logger = mock<EventLog>()
- private val actionLabel = "Action label"
- private val modifyShareLabel = "Modify share"
- 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 val resultConsumer =
- object : Consumer<Int> {
- var latestReturn = Integer.MIN_VALUE
-
- override fun accept(resultCode: Int) {
- latestReturn = resultCode
- }
- }
-
- @Before
- fun setup() {
- context.registerReceiver(testReceiver, IntentFilter(testAction), RECEIVER_EXPORTED)
- }
-
- @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 pending intent has been called
- assertTrue("Timed out waiting for broadcast", countdown.await(2500, TimeUnit.MILLISECONDS))
- }
-
- @Test
- fun testNoModifyShareAction() {
- val factory = createFactory(includeModifyShare = false)
-
- assertThat(factory.modifyShareAction).isNull()
- }
-
- @Test
- fun testModifyShareAction() {
- val factory = createFactory(includeModifyShare = true)
-
- val action = factory.modifyShareAction ?: error("Modify share action should not be null")
- action.onClicked.run()
-
- Mockito.verify(logger).logActionSelected(eq(EventLog.SELECTION_TYPE_MODIFY_SHARE))
- assertEquals(Activity.RESULT_OK, resultConsumer.latestReturn)
- // Verify the pending intent has been called
- assertTrue("Timed out waiting for broadcast", countdown.await(2500, TimeUnit.MILLISECONDS))
- }
-
- @Test
- fun nonSendAction_noCopyRunnable() {
- val targetIntent =
- Intent(Intent.ACTION_SEND_MULTIPLE).apply {
- putExtra(Intent.EXTRA_TEXT, "Text to show")
- }
-
- val chooserRequest =
- mock<ChooserRequestParameters> {
- whenever(this.targetIntent).thenReturn(targetIntent)
- whenever(chooserActions).thenReturn(ImmutableList.of())
- }
- val testSubject =
- ChooserActionFactory(
- context,
- chooserRequest,
- mock(),
- logger,
- {},
- { null },
- mock(),
- {},
- )
- assertThat(testSubject.copyButtonRunnable).isNull()
- }
-
- @Test
- fun sendActionNoText_noCopyRunnable() {
- val targetIntent = Intent(Intent.ACTION_SEND)
-
- val chooserRequest =
- mock<ChooserRequestParameters> {
- whenever(this.targetIntent).thenReturn(targetIntent)
- whenever(chooserActions).thenReturn(ImmutableList.of())
- }
- val testSubject =
- ChooserActionFactory(
- context,
- chooserRequest,
- mock(),
- logger,
- {},
- { null },
- mock(),
- {},
- )
- assertThat(testSubject.copyButtonRunnable).isNull()
- }
-
- @Test
- fun sendActionWithText_nonNullCopyRunnable() {
- val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_TEXT, "Text") }
-
- val chooserRequest =
- mock<ChooserRequestParameters> {
- whenever(this.targetIntent).thenReturn(targetIntent)
- whenever(chooserActions).thenReturn(ImmutableList.of())
- }
- val testSubject =
- ChooserActionFactory(
- context,
- chooserRequest,
- mock(),
- logger,
- {},
- { null },
- mock(),
- {},
- )
- assertThat(testSubject.copyButtonRunnable).isNotNull()
- }
-
- private fun createFactory(includeModifyShare: Boolean = false): ChooserActionFactory {
- val testPendingIntent =
- PendingIntent.getBroadcast(context, 0, Intent(testAction), PendingIntent.FLAG_IMMUTABLE)
- 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) {
- val modifyShare =
- ChooserAction.Builder(
- Icon.createWithResource("", Resources.ID_NULL),
- modifyShareLabel,
- testPendingIntent
- )
- .build()
- whenever(chooserRequest.modifyShareAction).thenReturn(modifyShare)
- }
-
- return ChooserActionFactory(
- context,
- chooserRequest,
- mock(),
- logger,
- {},
- { null },
- mock(),
- resultConsumer
- )
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java b/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java
deleted file mode 100644
index 3ee80c14..00000000
--- a/java/tests/src/com/android/intentresolver/ChooserActivityOverrideData.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2021 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 static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.os.UserHandle;
-
-import com.android.intentresolver.chooser.TargetInfo;
-import com.android.intentresolver.contentpreview.ImageLoader;
-import com.android.intentresolver.emptystate.CrossProfileIntentsChecker;
-import com.android.intentresolver.shortcuts.ShortcutLoader;
-
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-import kotlin.jvm.functions.Function2;
-
-/**
- * Singleton providing overrides to be applied by any {@code IChooserWrapper} used in testing.
- * We cannot directly mock the activity created since instrumentation creates it, so instead we use
- * this singleton to modify behavior.
- */
-public class ChooserActivityOverrideData {
- private static ChooserActivityOverrideData sInstance = null;
-
- public static ChooserActivityOverrideData getInstance() {
- if (sInstance == null) {
- sInstance = new ChooserActivityOverrideData();
- }
- return sInstance;
- }
-
- @SuppressWarnings("Since15")
- public Function<PackageManager, PackageManager> createPackageManager;
- public Function<TargetInfo, Boolean> onSafelyStartInternalCallback;
- public Function<TargetInfo, Boolean> onSafelyStartCallback;
- public Function2<UserHandle, Consumer<ShortcutLoader.Result>, ShortcutLoader>
- shortcutLoaderFactory = (userHandle, callback) -> null;
- public ChooserActivity.ChooserListController resolverListController;
- public ChooserActivity.ChooserListController workResolverListController;
- public Boolean isVoiceInteraction;
- public Cursor resolverCursor;
- public boolean resolverForceException;
- public ImageLoader imageLoader;
- public int alternateProfileSetting;
- public Resources resources;
- public AnnotatedUserHandles annotatedUserHandles;
- public boolean hasCrossProfileIntents;
- public boolean isQuietModeEnabled;
- public Integer myUserId;
- public WorkProfileAvailabilityManager mWorkProfileAvailability;
- public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
- public PackageManager packageManager;
-
- public void reset() {
- onSafelyStartInternalCallback = null;
- isVoiceInteraction = null;
- createPackageManager = null;
- imageLoader = null;
- resolverCursor = null;
- resolverForceException = false;
- resolverListController = mock(ChooserActivity.ChooserListController.class);
- workResolverListController = mock(ChooserActivity.ChooserListController.class);
- alternateProfileSetting = 0;
- resources = null;
- annotatedUserHandles = AnnotatedUserHandles.newBuilder()
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(UserHandle.SYSTEM)
- .setPersonalProfileUserHandle(UserHandle.SYSTEM)
- .build();
- hasCrossProfileIntents = true;
- isQuietModeEnabled = false;
- myUserId = null;
- packageManager = null;
- mWorkProfileAvailability = new WorkProfileAvailabilityManager(null, null, null) {
- @Override
- public boolean isQuietModeEnabled() {
- return isQuietModeEnabled;
- }
-
- @Override
- public boolean isWorkProfileUserUnlocked() {
- return true;
- }
-
- @Override
- public void requestQuietModeEnabled(boolean enabled) {
- isQuietModeEnabled = enabled;
- }
-
- @Override
- public void markWorkProfileEnabledBroadcastReceived() {}
-
- @Override
- public boolean isWaitingToEnableWorkProfile() {
- return false;
- }
- };
- shortcutLoaderFactory = ((userHandle, resultConsumer) -> null);
-
- mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class);
- when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt()))
- .thenAnswer(invocation -> hasCrossProfileIntents);
- }
-
- private ChooserActivityOverrideData() {}
-}
-
diff --git a/java/tests/src/com/android/intentresolver/ChooserIntegratedDeviceComponentsTest.kt b/java/tests/src/com/android/intentresolver/ChooserIntegratedDeviceComponentsTest.kt
deleted file mode 100644
index 9a5dabdb..00000000
--- a/java/tests/src/com/android/intentresolver/ChooserIntegratedDeviceComponentsTest.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver
-
-import android.content.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/ChooserListAdapterDataTest.kt b/java/tests/src/com/android/intentresolver/ChooserListAdapterDataTest.kt
deleted file mode 100644
index e5927e36..00000000
--- a/java/tests/src/com/android/intentresolver/ChooserListAdapterDataTest.kt
+++ /dev/null
@@ -1,179 +0,0 @@
-package com.android.intentresolver
-
-import android.content.Context
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ComponentInfoFlags
-import android.os.UserHandle
-import android.os.UserManager
-import android.view.LayoutInflater
-import com.android.intentresolver.ResolverDataProvider.createActivityInfo
-import com.android.intentresolver.ResolverDataProvider.createResolvedComponentInfo
-import com.android.intentresolver.icons.TargetDataLoader
-import com.android.intentresolver.logging.FakeEventLog
-import com.android.intentresolver.util.TestExecutor
-import com.android.internal.logging.InstanceId
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.mockito.Mockito
-
-class ChooserListAdapterDataTest {
- private val layoutInflater = mock<LayoutInflater>()
- private val packageManager = mock<PackageManager>()
- private val userManager = mock<UserManager> { whenever(isManagedProfile).thenReturn(false) }
- private val resources =
- mock<android.content.res.Resources> {
- whenever(getInteger(R.integer.config_maxShortcutTargetsPerApp)).thenReturn(2)
- }
- private val context =
- mock<Context> {
- whenever(getSystemService(Context.LAYOUT_INFLATER_SERVICE)).thenReturn(layoutInflater)
- whenever(getSystemService(Context.USER_SERVICE)).thenReturn(userManager)
- whenever(packageManager).thenReturn(this@ChooserListAdapterDataTest.packageManager)
- whenever(resources).thenReturn(this@ChooserListAdapterDataTest.resources)
- }
- private val targetIntent = Intent(Intent.ACTION_SEND)
- private val payloadIntents = listOf(targetIntent)
- private val resolverListController =
- mock<ResolverListController> {
- whenever(filterIneligibleActivities(any(), Mockito.anyBoolean())).thenReturn(null)
- whenever(filterLowPriority(any(), Mockito.anyBoolean())).thenReturn(null)
- }
- private val resolverListCommunicator = FakeResolverListCommunicator()
- private val userHandle = UserHandle.of(UserHandle.USER_CURRENT)
- private val targetDataLoader = mock<TargetDataLoader>()
- private val backgroundExecutor = TestExecutor()
- private val immediateExecutor = TestExecutor(immediate = true)
- private val chooserRequestParams =
- ChooserRequestParameters(
- Intent.createChooser(targetIntent, ""),
- "org.referrer.package",
- null
- )
-
- @Test
- fun test_twoTargetsWithNonOverlappingInitialIntent_threeTargetsInResolverAdapter() {
- val resolvedTargets =
- listOf(
- createResolvedComponentInfo(1),
- createResolvedComponentInfo(2),
- )
- val targetIntent = Intent(Intent.ACTION_SEND)
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- val initialActivityInfo = createActivityInfo(3)
- val initialIntents =
- arrayOf(
- Intent(Intent.ACTION_SEND).apply { component = initialActivityInfo.componentName }
- )
- whenever(
- packageManager.getActivityInfo(
- eq(initialActivityInfo.componentName),
- any<ComponentInfoFlags>()
- )
- )
- .thenReturn(initialActivityInfo)
- val testSubject =
- ChooserListAdapter(
- context,
- payloadIntents,
- initialIntents,
- /*rList=*/ null,
- /*filterLastUsed=*/ false,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- packageManager,
- FakeEventLog(InstanceId.fakeInstanceId(1)),
- chooserRequestParams,
- /*maxRankedTargets=*/ 2,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- val isLoaded = testSubject.rebuildList(doPostProcessing)
-
- assertThat(isLoaded).isFalse()
- assertThat(testSubject.displayResolveInfoCount).isEqualTo(0)
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(1)
-
- backgroundExecutor.runUntilIdle()
-
- // we don't reset placeholder count (legacy logic, likely an oversight?)
- assertThat(testSubject.displayResolveInfoCount).isEqualTo(resolvedTargets.size)
- }
-
- @Test
- fun test_twoTargetsWithOverlappingInitialIntent_oneTargetsInResolverAdapter() {
- val resolvedTargets =
- listOf(
- createResolvedComponentInfo(1),
- createResolvedComponentInfo(2),
- )
- val targetIntent = Intent(Intent.ACTION_SEND)
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- val activityInfo = resolvedTargets[1].getResolveInfoAt(0).activityInfo
- val initialIntents =
- arrayOf(Intent(Intent.ACTION_SEND).apply { component = activityInfo.componentName })
- whenever(
- packageManager.getActivityInfo(
- eq(activityInfo.componentName),
- any<ComponentInfoFlags>()
- )
- )
- .thenReturn(activityInfo)
- val testSubject =
- ChooserListAdapter(
- context,
- payloadIntents,
- initialIntents,
- /*rList=*/ null,
- /*filterLastUsed=*/ false,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- packageManager,
- FakeEventLog(InstanceId.fakeInstanceId(1)),
- chooserRequestParams,
- /*maxRankedTargets=*/ 2,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- val isLoaded = testSubject.rebuildList(doPostProcessing)
-
- assertThat(isLoaded).isFalse()
- assertThat(testSubject.displayResolveInfoCount).isEqualTo(0)
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(1)
-
- backgroundExecutor.runUntilIdle()
-
- // we don't reset placeholder count (legacy logic, likely an oversight?)
- assertThat(testSubject.displayResolveInfoCount).isEqualTo(resolvedTargets.size - 1)
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt b/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt
deleted file mode 100644
index a4078365..00000000
--- a/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * 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.content.ComponentName
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ResolveInfoFlags
-import android.content.pm.ShortcutInfo
-import android.os.UserHandle
-import android.view.View
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.intentresolver.chooser.DisplayResolveInfo
-import com.android.intentresolver.chooser.SelectableTargetInfo
-import com.android.intentresolver.chooser.TargetInfo
-import com.android.intentresolver.icons.TargetDataLoader
-import com.android.intentresolver.logging.EventLogImpl
-import com.android.internal.R
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-
-@RunWith(AndroidJUnit4::class)
-class ChooserListAdapterTest {
- private val userHandle: UserHandle =
- InstrumentationRegistry.getInstrumentation().targetContext.user
-
- private val packageManager =
- mock<PackageManager> {
- whenever(resolveActivity(any(), any<ResolveInfoFlags>())).thenReturn(mock())
- }
- private val context = InstrumentationRegistry.getInstrumentation().context
- private val resolverListController = mock<ResolverListController>()
- private val appLabel = "App"
- private val targetLabel = "Target"
- private val mEventLog = mock<EventLogImpl>()
- private val mTargetDataLoader = mock<TargetDataLoader>()
-
- private val testSubject by lazy {
- ChooserListAdapter(
- context,
- emptyList(),
- emptyArray(),
- emptyList(),
- false,
- resolverListController,
- userHandle,
- Intent(),
- mock(),
- packageManager,
- mEventLog,
- mock(),
- 0,
- null,
- mTargetDataLoader
- )
- }
-
- @Before
- fun setup() {
- // ChooserListAdapter reads DeviceConfig and needs a permission for that.
- InstrumentationRegistry.getInstrumentation()
- .uiAutomation
- .adoptShellPermissionIdentity("android.permission.READ_DEVICE_CONFIG")
- }
-
- @Test
- fun testDirectShareTargetLoadingIconIsStarted() {
- val view = createView()
- val viewHolder = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolder
- val targetInfo = createSelectableTargetInfo()
- testSubject.onBindView(view, targetInfo, 0)
-
- verify(mTargetDataLoader, times(1)).loadDirectShareIcon(any(), any(), any())
- }
-
- @Test
- fun onBindView_DirectShareTargetIconAndLabelLoadedOnlyOnce() {
- val view = createView()
- val viewHolderOne = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolderOne
- val targetInfo = createSelectableTargetInfo()
- testSubject.onBindView(view, targetInfo, 0)
-
- val viewHolderTwo = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolderTwo
-
- testSubject.onBindView(view, targetInfo, 0)
-
- verify(mTargetDataLoader, times(1)).loadDirectShareIcon(any(), any(), any())
- }
-
- @Test
- fun onBindView_AppTargetIconAndLabelLoadedOnlyOnce() {
- val view = createView()
- val viewHolderOne = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolderOne
- val targetInfo =
- DisplayResolveInfo.newDisplayResolveInfo(
- Intent(),
- ResolverDataProvider.createResolveInfo(2, 0, userHandle),
- null,
- "extended info",
- Intent()
- )
- testSubject.onBindView(view, targetInfo, 0)
-
- val viewHolderTwo = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolderTwo
-
- testSubject.onBindView(view, targetInfo, 0)
-
- verify(mTargetDataLoader, times(1)).loadAppTargetIcon(any(), any(), any())
- }
-
- @Test
- fun onBindView_contentDescription() {
- val view = createView()
- val viewHolder = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolder
- val targetInfo = createSelectableTargetInfo()
- testSubject.onBindView(view, targetInfo, 0)
-
- assertThat(view.contentDescription).isEqualTo("$targetLabel $appLabel")
- }
-
- @Test
- fun onBindView_contentDescriptionPinned() {
- val view = createView()
- val viewHolder = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolder
- val targetInfo = createSelectableTargetInfo(true)
- testSubject.onBindView(view, targetInfo, 0)
-
- assertThat(view.contentDescription).isEqualTo("$targetLabel $appLabel. Pinned")
- }
-
- @Test
- fun onBindView_displayInfoContentDescriptionPinned() {
- val view = createView()
- val viewHolder = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolder
- val targetInfo = createDisplayResolveInfo(isPinned = true)
- testSubject.onBindView(view, targetInfo, 0)
-
- assertThat(view.contentDescription).isEqualTo("$appLabel. Pinned")
- }
-
- private fun createSelectableTargetInfo(isPinned: Boolean = false): TargetInfo {
- val shortcutInfo =
- createShortcutInfo("id-1", ComponentName("pkg", "Class"), 1).apply {
- if (isPinned) {
- addFlags(ShortcutInfo.FLAG_PINNED)
- }
- }
- return SelectableTargetInfo.newSelectableTargetInfo(
- /* sourceInfo = */ createDisplayResolveInfo(isPinned),
- /* backupResolveInfo = */ mock(),
- /* resolvedIntent = */ Intent(),
- /* chooserTarget = */ createChooserTarget(
- targetLabel,
- 0.5f,
- ComponentName("pkg", "Class"),
- "id-1"
- ),
- /* modifiedScore = */ 1f,
- shortcutInfo,
- /* appTarget */ null,
- /* referrerFillInIntent = */ Intent()
- )
- }
-
- private fun createDisplayResolveInfo(isPinned: Boolean = false): DisplayResolveInfo =
- DisplayResolveInfo.newDisplayResolveInfo(
- Intent(),
- ResolverDataProvider.createResolveInfo(2, 0, userHandle),
- appLabel,
- "extended info",
- Intent(),
- )
- .apply {
- if (isPinned) {
- setPinned(true)
- }
- }
-
- private fun createView(): View {
- val view = FrameLayout(context)
- TextView(context).apply {
- id = R.id.text1
- view.addView(this)
- }
- TextView(context).apply {
- id = R.id.text2
- view.addView(this)
- }
- ImageView(context).apply {
- id = R.id.icon
- view.addView(this)
- }
- return view
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt b/java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt
deleted file mode 100644
index 61ac0c21..00000000
--- a/java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver
-
-import android.app.Activity
-import android.app.Application
-import android.content.Intent
-import android.content.IntentSender
-import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
-import android.os.Message
-import android.os.ResultReceiver
-import androidx.lifecycle.Observer
-import androidx.test.annotation.UiThreadTest
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.intentresolver.ChooserRefinementManager.RefinementCompletion
-import com.android.intentresolver.chooser.ImmutableTargetInfo
-import com.android.intentresolver.chooser.TargetInfo
-import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Mockito
-
-@RunWith(AndroidJUnit4::class)
-@UiThreadTest
-class ChooserRefinementManagerTest {
- private val refinementManager = ChooserRefinementManager()
- private val intentSender = mock<IntentSender>()
- private val application = mock<Application>()
- private val exampleSourceIntents =
- listOf(Intent(Intent.ACTION_VIEW), Intent(Intent.ACTION_EDIT))
- private val exampleTargetInfo =
- ImmutableTargetInfo.newBuilder().setAllSourceIntents(exampleSourceIntents).build()
-
- private val completionObserver =
- object : Observer<RefinementCompletion> {
- val failureCountDown = CountDownLatch(1)
- val successCountDown = CountDownLatch(1)
- var latestTargetInfo: TargetInfo? = null
-
- override fun onChanged(completion: RefinementCompletion) {
- if (completion.consume()) {
- val targetInfo = completion.targetInfo
- if (targetInfo == null) {
- failureCountDown.countDown()
- } else {
- latestTargetInfo = targetInfo
- successCountDown.countDown()
- }
- }
- }
- }
-
- /** Synchronously executes post() calls. */
- private class FakeHandler(looper: Looper) : Handler(looper) {
- override fun sendMessageAtTime(msg: Message, uptimeMillis: Long): Boolean {
- dispatchMessage(msg)
- return true
- }
- }
-
- @Before
- fun setup() {
- refinementManager.refinementCompletion.observeForever(completionObserver)
- }
-
- @Test
- fun testTypicalRefinementFlow() {
- assertThat(
- refinementManager.maybeHandleSelection(
- exampleTargetInfo,
- intentSender,
- application,
- FakeHandler(checkNotNull(Looper.myLooper()))
- )
- )
- .isTrue()
-
- val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
- Mockito.verify(intentSender)
- .sendIntent(any(), eq(0), intentCaptor.capture(), eq(null), eq(null))
-
- val intent = intentCaptor.value
- assertThat(intent?.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java))
- .isEqualTo(exampleSourceIntents[0])
-
- val alternates =
- intent?.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS, Intent::class.java)
- assertThat(alternates?.size).isEqualTo(1)
- assertThat(alternates?.get(0)).isEqualTo(exampleSourceIntents[1])
-
- // Complete the refinement
- val receiver =
- intent?.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER, ResultReceiver::class.java)
- val bundle = Bundle().apply { putParcelable(Intent.EXTRA_INTENT, exampleSourceIntents[0]) }
- receiver?.send(Activity.RESULT_OK, bundle)
-
- assertThat(completionObserver.successCountDown.await(1000, TimeUnit.MILLISECONDS)).isTrue()
- assertThat(completionObserver.latestTargetInfo?.resolvedIntent?.action)
- .isEqualTo(Intent.ACTION_VIEW)
- }
-
- @Test
- fun testRefinementCancelled() {
- assertThat(
- refinementManager.maybeHandleSelection(
- exampleTargetInfo,
- intentSender,
- application,
- FakeHandler(checkNotNull(Looper.myLooper()))
- )
- )
- .isTrue()
-
- val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
- Mockito.verify(intentSender)
- .sendIntent(any(), eq(0), intentCaptor.capture(), eq(null), eq(null))
-
- val intent = intentCaptor.value
-
- // Complete the refinement
- val receiver =
- intent?.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER, ResultReceiver::class.java)
- val bundle = Bundle().apply { putParcelable(Intent.EXTRA_INTENT, exampleSourceIntents[0]) }
- receiver?.send(Activity.RESULT_CANCELED, bundle)
-
- assertThat(completionObserver.failureCountDown.await(1000, TimeUnit.MILLISECONDS)).isTrue()
- }
-
- @Test
- fun testMaybeHandleSelection_noSourceIntents() {
- assertThat(
- refinementManager.maybeHandleSelection(
- ImmutableTargetInfo.newBuilder().build(),
- intentSender,
- application,
- FakeHandler(checkNotNull(Looper.myLooper()))
- )
- )
- .isFalse()
- }
-
- @Test
- fun testMaybeHandleSelection_suspended() {
- val targetInfo =
- ImmutableTargetInfo.newBuilder()
- .setAllSourceIntents(exampleSourceIntents)
- .setIsSuspended(true)
- .build()
-
- assertThat(
- refinementManager.maybeHandleSelection(
- targetInfo,
- intentSender,
- application,
- FakeHandler(checkNotNull(Looper.myLooper()))
- )
- )
- .isFalse()
- }
-
- @Test
- fun testMaybeHandleSelection_noIntentSender() {
- assertThat(
- refinementManager.maybeHandleSelection(
- exampleTargetInfo,
- /* IntentSender */ null,
- application,
- FakeHandler(checkNotNull(Looper.myLooper()))
- )
- )
- .isFalse()
- }
-
- @Test
- fun testConfigurationChangeDuringRefinement() {
- assertThat(
- refinementManager.maybeHandleSelection(
- exampleTargetInfo,
- intentSender,
- application,
- FakeHandler(checkNotNull(Looper.myLooper()))
- )
- )
- .isTrue()
-
- refinementManager.onActivityStop(/* config changing = */ true)
- refinementManager.onActivityResume()
-
- assertThat(completionObserver.failureCountDown.count).isEqualTo(1)
- }
-
- @Test
- fun testResumeDuringRefinement() {
- assertThat(
- refinementManager.maybeHandleSelection(
- exampleTargetInfo,
- intentSender,
- application,
- FakeHandler(checkNotNull(Looper.myLooper())!!)
- )
- )
- .isTrue()
-
- refinementManager.onActivityStop(/* config changing = */ false)
- // Resume during refinement but not during a config change, so finish the activity.
- refinementManager.onActivityResume()
-
- // Call should be synchronous, don't need to await for this one.
- assertThat(completionObserver.failureCountDown.count).isEqualTo(0)
- }
-
- @Test
- fun testRefinementCompletion() {
- val refinementCompletion = RefinementCompletion(exampleTargetInfo)
- assertThat(refinementCompletion.targetInfo).isEqualTo(exampleTargetInfo)
- assertThat(refinementCompletion.consume()).isTrue()
- assertThat(refinementCompletion.targetInfo).isEqualTo(exampleTargetInfo)
-
- // can only consume once.
- assertThat(refinementCompletion.consume()).isFalse()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/ChooserRequestParametersTest.kt b/java/tests/src/com/android/intentresolver/ChooserRequestParametersTest.kt
deleted file mode 100644
index 90f6cf93..00000000
--- a/java/tests/src/com/android/intentresolver/ChooserRequestParametersTest.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver
-
-import android.app.PendingIntent
-import android.content.Intent
-import android.graphics.drawable.Icon
-import android.net.Uri
-import android.service.chooser.ChooserAction
-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 ChooserRequestParametersTest {
-
- @Test
- fun testChooserActions() {
- val actionCount = 3
- val intent = Intent(Intent.ACTION_SEND)
- val actions = createChooserActions(actionCount)
- val chooserIntent =
- Intent(Intent.ACTION_CHOOSER).apply {
- putExtra(Intent.EXTRA_INTENT, intent)
- putExtra(Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS, actions)
- }
- val request = ChooserRequestParameters(chooserIntent, "", Uri.EMPTY)
- assertThat(request.chooserActions).containsExactlyElementsIn(actions).inOrder()
- }
-
- @Test
- fun testChooserActions_empty() {
- val intent = Intent(Intent.ACTION_SEND)
- val chooserIntent =
- Intent(Intent.ACTION_CHOOSER).apply { putExtra(Intent.EXTRA_INTENT, intent) }
- val request = ChooserRequestParameters(chooserIntent, "", Uri.EMPTY)
- assertThat(request.chooserActions).isEmpty()
- }
-
- @Test
- fun testChooserActions_tooMany() {
- val intent = Intent(Intent.ACTION_SEND)
- val chooserActions = createChooserActions(10)
- val chooserIntent =
- Intent(Intent.ACTION_CHOOSER).apply {
- putExtra(Intent.EXTRA_INTENT, intent)
- putExtra(Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS, chooserActions)
- }
-
- val request = ChooserRequestParameters(chooserIntent, "", Uri.EMPTY)
-
- val expectedActions = chooserActions.sliceArray(0 until 5)
- assertThat(request.chooserActions).containsExactlyElementsIn(expectedActions).inOrder()
- }
-
- private fun createChooserActions(count: Int): Array<ChooserAction> {
- return Array(count) { i -> createChooserAction("$i") }
- }
-
- private fun createChooserAction(label: CharSequence): ChooserAction {
- val icon = Icon.createWithContentUri("content://org.package.app/image")
- val pendingIntent =
- PendingIntent.getBroadcast(
- InstrumentationRegistry.getInstrumentation().getTargetContext(),
- 0,
- Intent("TESTACTION"),
- PendingIntent.FLAG_IMMUTABLE
- )
- return ChooserAction.Builder(icon, label, pendingIntent).build()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java
deleted file mode 100644
index 72f1f452..00000000
--- a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2008 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.prediction.AppPredictor;
-import android.app.usage.UsageStatsManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.UserHandle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.ViewModelProvider;
-
-import com.android.intentresolver.chooser.DisplayResolveInfo;
-import com.android.intentresolver.chooser.TargetInfo;
-import com.android.intentresolver.emptystate.CrossProfileIntentsChecker;
-import com.android.intentresolver.grid.ChooserGridAdapter;
-import com.android.intentresolver.icons.TargetDataLoader;
-import com.android.intentresolver.shortcuts.ShortcutLoader;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Simple wrapper around chooser activity to be able to initiate it under test. For more
- * information, see {@code com.android.internal.app.ChooserWrapperActivity}.
- */
-public class ChooserWrapperActivity extends ChooserActivity implements IChooserWrapper {
- static final ChooserActivityOverrideData sOverrides = ChooserActivityOverrideData.getInstance();
- private UsageStatsManager mUsm;
-
- // ResolverActivity (the base class of ChooserActivity) inspects the launched-from UID at
- // onCreate and needs to see some non-negative value in the test.
- @Override
- public int getLaunchedFromUid() {
- return 1234;
- }
-
- @Override
- public ChooserListAdapter createChooserListAdapter(
- Context context,
- List<Intent> payloadIntents,
- Intent[] initialIntents,
- List<ResolveInfo> rList,
- boolean filterLastUsed,
- ResolverListController resolverListController,
- UserHandle userHandle,
- Intent targetIntent,
- ChooserRequestParameters chooserRequest,
- int maxTargetsPerRow,
- TargetDataLoader targetDataLoader) {
- PackageManager packageManager =
- sOverrides.packageManager == null ? context.getPackageManager()
- : sOverrides.packageManager;
- return new ChooserListAdapter(
- context,
- payloadIntents,
- initialIntents,
- rList,
- filterLastUsed,
- createListController(userHandle),
- userHandle,
- targetIntent,
- this,
- packageManager,
- getEventLog(),
- chooserRequest,
- maxTargetsPerRow,
- userHandle,
- targetDataLoader);
- }
-
- @Override
- public ChooserListAdapter getAdapter() {
- return mChooserMultiProfilePagerAdapter.getActiveListAdapter();
- }
-
- @Override
- public ChooserListAdapter getPersonalListAdapter() {
- return ((ChooserGridAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(0))
- .getListAdapter();
- }
-
- @Override
- public ChooserListAdapter getWorkListAdapter() {
- if (mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
- return null;
- }
- return ((ChooserGridAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(1))
- .getListAdapter();
- }
-
- @Override
- public boolean getIsSelected() {
- return mIsSuccessfullySelected;
- }
-
- @Override
- 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
- public UsageStatsManager getUsageStatsManager() {
- if (mUsm == null) {
- mUsm = getSystemService(UsageStatsManager.class);
- }
- return mUsm;
- }
-
- @Override
- public boolean isVoiceInteraction() {
- if (sOverrides.isVoiceInteraction != null) {
- return sOverrides.isVoiceInteraction;
- }
- return super.isVoiceInteraction();
- }
-
- @Override
- protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
- if (sOverrides.mCrossProfileIntentsChecker != null) {
- return sOverrides.mCrossProfileIntentsChecker;
- }
- return super.createCrossProfileIntentsChecker();
- }
-
- @Override
- protected WorkProfileAvailabilityManager createWorkProfileAvailabilityManager() {
- if (sOverrides.mWorkProfileAvailability != null) {
- return sOverrides.mWorkProfileAvailability;
- }
- return super.createWorkProfileAvailabilityManager();
- }
-
- @Override
- public void safelyStartActivityInternal(TargetInfo cti, UserHandle user,
- @Nullable Bundle options) {
- if (sOverrides.onSafelyStartInternalCallback != null
- && sOverrides.onSafelyStartInternalCallback.apply(cti)) {
- return;
- }
- super.safelyStartActivityInternal(cti, user, options);
- }
-
- @Override
- protected ChooserListController createListController(UserHandle userHandle) {
- if (userHandle == UserHandle.SYSTEM) {
- return sOverrides.resolverListController;
- }
- return sOverrides.workResolverListController;
- }
-
- @Override
- public PackageManager getPackageManager() {
- if (sOverrides.createPackageManager != null) {
- return sOverrides.createPackageManager.apply(super.getPackageManager());
- }
- return super.getPackageManager();
- }
-
- @Override
- public Resources getResources() {
- if (sOverrides.resources != null) {
- return sOverrides.resources;
- }
- return super.getResources();
- }
-
- @Override
- protected ViewModelProvider.Factory createPreviewViewModelFactory() {
- return TestContentPreviewViewModel.Companion.wrap(
- super.createPreviewViewModelFactory(),
- sOverrides.imageLoader);
- }
-
- @Override
- public Cursor queryResolver(ContentResolver resolver, Uri uri) {
- if (sOverrides.resolverCursor != null) {
- return sOverrides.resolverCursor;
- }
-
- if (sOverrides.resolverForceException) {
- throw new SecurityException("Test exception handling");
- }
-
- return super.queryResolver(resolver, uri);
- }
-
- @Override
- protected boolean isWorkProfile() {
- if (sOverrides.alternateProfileSetting != 0) {
- return sOverrides.alternateProfileSetting == MetricsEvent.MANAGED_PROFILE;
- }
- return super.isWorkProfile();
- }
-
- @Override
- public DisplayResolveInfo createTestDisplayResolveInfo(
- Intent originalIntent,
- ResolveInfo pri,
- CharSequence pLabel,
- CharSequence pInfo,
- Intent replacementIntent) {
- return DisplayResolveInfo.newDisplayResolveInfo(
- originalIntent,
- pri,
- pLabel,
- pInfo,
- replacementIntent);
- }
-
- @Override
- protected AnnotatedUserHandles computeAnnotatedUserHandles() {
- return sOverrides.annotatedUserHandles;
- }
-
- @Override
- public UserHandle getCurrentUserHandle() {
- return mMultiProfilePagerAdapter.getCurrentUserHandle();
- }
-
- @NonNull
- @Override
- public Context createContextAsUser(UserHandle user, int flags) {
- // return the current context as a work profile doesn't really exist in these tests
- return this;
- }
-
- @Override
- protected ShortcutLoader createShortcutLoader(
- Context context,
- AppPredictor appPredictor,
- UserHandle userHandle,
- IntentFilter targetIntentFilter,
- Consumer<ShortcutLoader.Result> callback) {
- ShortcutLoader shortcutLoader =
- sOverrides.shortcutLoaderFactory.invoke(userHandle, callback);
- if (shortcutLoader != null) {
- return shortcutLoader;
- }
- return super.createShortcutLoader(
- context, appPredictor, userHandle, targetIntentFilter, callback);
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/EnterTransitionAnimationDelegateTest.kt b/java/tests/src/com/android/intentresolver/EnterTransitionAnimationDelegateTest.kt
deleted file mode 100644
index c7d20000..00000000
--- a/java/tests/src/com/android/intentresolver/EnterTransitionAnimationDelegateTest.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver
-
-import android.content.res.Resources
-import android.view.View
-import android.view.Window
-import androidx.activity.ComponentActivity
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.testing.TestLifecycleOwner
-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.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
- }
-
- @After
- fun cleanup() {
- lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
- 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/FakeResolverListCommunicator.kt b/java/tests/src/com/android/intentresolver/FakeResolverListCommunicator.kt
deleted file mode 100644
index 5e9cd98f..00000000
--- a/java/tests/src/com/android/intentresolver/FakeResolverListCommunicator.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver
-
-import android.content.Intent
-import android.content.pm.ActivityInfo
-import java.util.concurrent.atomic.AtomicInteger
-
-class FakeResolverListCommunicator(private val layoutWithDefaults: Boolean = true) :
- ResolverListAdapter.ResolverListCommunicator {
- private val sendVoiceCounter = AtomicInteger()
- private val updateProfileViewButtonCounter = AtomicInteger()
-
- val sendVoiceCommandCount
- get() = sendVoiceCounter.get()
- val updateProfileViewButtonCount
- get() = updateProfileViewButtonCounter.get()
-
- override fun getReplacementIntent(activityInfo: ActivityInfo?, defIntent: Intent): Intent {
- return defIntent
- }
-
- override fun onPostListReady(
- listAdapter: ResolverListAdapter?,
- updateUi: Boolean,
- rebuildCompleted: Boolean,
- ) = Unit
-
- override fun sendVoiceChoicesIfNeeded() {
- sendVoiceCounter.incrementAndGet()
- }
-
- override fun updateProfileViewButton() {
- updateProfileViewButtonCounter.incrementAndGet()
- }
-
- override fun useLayoutWithDefault(): Boolean = layoutWithDefaults
-
- override fun shouldGetActivityMetadata(): Boolean = true
-
- override fun onHandlePackagesChanged(listAdapter: ResolverListAdapter?) {}
-}
diff --git a/java/tests/src/com/android/intentresolver/IChooserWrapper.java b/java/tests/src/com/android/intentresolver/IChooserWrapper.java
deleted file mode 100644
index 481cf3b2..00000000
--- a/java/tests/src/com/android/intentresolver/IChooserWrapper.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2021 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.usage.UsageStatsManager;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.os.UserHandle;
-
-import androidx.annotation.Nullable;
-
-import com.android.intentresolver.chooser.DisplayResolveInfo;
-
-import java.util.concurrent.Executor;
-
-/**
- * Test-only extended API capabilities that an instrumented ChooserActivity subclass provides in
- * order to expose the internals for override/inspection. Implementations should apply the overrides
- * specified by the {@code ChooserActivityOverrideData} singleton.
- */
-public interface IChooserWrapper {
- ChooserListAdapter getAdapter();
- ChooserListAdapter getPersonalListAdapter();
- ChooserListAdapter getWorkListAdapter();
- boolean getIsSelected();
- UsageStatsManager getUsageStatsManager();
- DisplayResolveInfo createTestDisplayResolveInfo(
- Intent originalIntent,
- ResolveInfo pri,
- CharSequence pLabel,
- CharSequence pInfo,
- @Nullable Intent replacementIntent);
- UserHandle getCurrentUserHandle();
- Executor getMainExecutor();
-}
diff --git a/java/tests/src/com/android/intentresolver/MatcherUtils.java b/java/tests/src/com/android/intentresolver/MatcherUtils.java
deleted file mode 100644
index 97cc6984..00000000
--- a/java/tests/src/com/android/intentresolver/MatcherUtils.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2020 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 org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-
-/**
- * Utils for helping with more customized matching options, for example matching the first
- * occurrence of a set criteria.
- */
-public class MatcherUtils {
-
- /**
- * Returns a {@link Matcher} which only matches the first occurrence of a set criteria.
- */
- public static <T> Matcher<T> first(final Matcher<T> matcher) {
- return new BaseMatcher<T>() {
- boolean isFirstMatch = true;
-
- @Override
- public boolean matches(final Object item) {
- if (isFirstMatch && matcher.matches(item)) {
- isFirstMatch = false;
- return true;
- }
- return false;
- }
-
- @Override
- public void describeTo(final Description description) {
- description.appendText("Returns the first matching item");
- }
- };
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt b/java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt
deleted file mode 100644
index db9fbd93..00000000
--- a/java/tests/src/com/android/intentresolver/MockitoKotlinHelpers.kt
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * 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
-
-/**
- * Kotlin versions of popular mockito methods that can return null in situations when Kotlin expects
- * a non-null value. Kotlin will throw an IllegalStateException when this takes place ("x must not
- * be null"). To fix this, we can use methods that modify the return type to be nullable. This
- * causes Kotlin to skip the null checks.
- * Cloned from frameworks/base/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
- */
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatcher
-import org.mockito.ArgumentMatchers
-import org.mockito.MockSettings
-import org.mockito.Mockito
-import org.mockito.stubbing.Answer
-import org.mockito.stubbing.OngoingStubbing
-import org.mockito.stubbing.Stubber
-
-/**
- * Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when
- * null is returned.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-fun <T> eq(obj: T): T = Mockito.eq<T>(obj)
-
-/**
- * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when
- * null is returned.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
-inline fun <reified T> any(): T = any(T::class.java)
-
-/**
- * Returns Mockito.argThat() as nullable type to avoid java.lang.IllegalStateException when
- * null is returned.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-fun <T> argThat(matcher: ArgumentMatcher<T>): T = Mockito.argThat(matcher)
-
-/**
- * Kotlin type-inferred version of Mockito.nullable()
- */
-inline fun <reified T> nullable(): T? = Mockito.nullable(T::class.java)
-
-/**
- * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException
- * when null is returned.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
-
-/**
- * Helper function for creating an argumentCaptor in kotlin.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-inline fun <reified T : Any> argumentCaptor(): ArgumentCaptor<T> =
- ArgumentCaptor.forClass(T::class.java)
-
-/**
- * Helper function for creating new mocks, without the need to pass in a [Class] instance.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- *
- * @param apply builder function to simplify stub configuration by improving type inference.
- */
-inline fun <reified T : Any> mock(
- mockSettings: MockSettings = Mockito.withSettings(),
- apply: T.() -> Unit = {}
-): T = Mockito.mock(T::class.java, mockSettings).apply(apply)
-
-/**
- * Helper function for stubbing methods without the need to use backticks.
- *
- * @see Mockito.when
- */
-fun <T> whenever(methodCall: T): OngoingStubbing<T> = Mockito.`when`(methodCall)
-
-/**
- * Helper function for stubbing methods without the need to use backticks.
- */
-fun <T> Stubber.whenever(mock: T): T = `when`(mock)
-
-/**
- * A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when
- * kotlin tests are mocking kotlin objects and the methods take non-null parameters:
- *
- * java.lang.NullPointerException: capture() must not be null
- */
-class KotlinArgumentCaptor<T> constructor(clazz: Class<T>) {
- private val wrapped: ArgumentCaptor<T> = ArgumentCaptor.forClass(clazz)
- fun capture(): T = wrapped.capture()
- val value: T
- get() = wrapped.value
- val allValues: List<T>
- get() = wrapped.allValues
-}
-
-/**
- * Helper function for creating an argumentCaptor in kotlin.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-inline fun <reified T : Any> kotlinArgumentCaptor(): KotlinArgumentCaptor<T> =
- KotlinArgumentCaptor(T::class.java)
-
-/**
- * Helper function for creating and using a single-use ArgumentCaptor in kotlin.
- *
- * val captor = argumentCaptor<Foo>()
- * verify(...).someMethod(captor.capture())
- * val captured = captor.value
- *
- * becomes:
- *
- * val captured = withArgCaptor<Foo> { verify(...).someMethod(capture()) }
- *
- * NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException.
- */
-inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T =
- kotlinArgumentCaptor<T>().apply { block() }.value
-
-/**
- * Variant of [withArgCaptor] for capturing multiple arguments.
- *
- * val captor = argumentCaptor<Foo>()
- * verify(...).someMethod(captor.capture())
- * val captured: List<Foo> = captor.allValues
- *
- * becomes:
- *
- * val capturedList = captureMany<Foo> { verify(...).someMethod(capture()) }
- */
-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 })
-
-/**
- * Intended as a default Answer for a mock to prevent dependence on defaults.
- *
- * Use as:
- * ```
- * val context = mock<Context>(withSettings()
- * .defaultAnswer(THROWS_EXCEPTION))
- * ```
- *
- * To avoid triggering the exception during stubbing, must ONLY use one of the doXXX() methods, such
- * as:
- * * [doAnswer][Mockito.doAnswer]
- * * [doCallRealMethod][Mockito.doCallRealMethod]
- * * [doNothing][Mockito.doNothing]
- * * [doReturn][Mockito.doReturn]
- * * [doThrow][Mockito.doThrow]
- */
-val THROWS_EXCEPTION = Answer { error("Unstubbed behavior was accessed.") }
diff --git a/java/tests/src/com/android/intentresolver/MultiProfilePagerAdapterTest.kt b/java/tests/src/com/android/intentresolver/MultiProfilePagerAdapterTest.kt
deleted file mode 100644
index ed06f7d1..00000000
--- a/java/tests/src/com/android/intentresolver/MultiProfilePagerAdapterTest.kt
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver
-
-import android.os.UserHandle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ListView
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.intentresolver.MultiProfilePagerAdapter.PROFILE_PERSONAL
-import com.android.intentresolver.MultiProfilePagerAdapter.PROFILE_WORK
-import com.android.intentresolver.emptystate.EmptyStateProvider
-import com.google.common.collect.ImmutableList
-import com.google.common.truth.Truth.assertThat
-import java.util.Optional
-import java.util.function.Supplier
-import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-
-class MultiProfilePagerAdapterTest {
- private val PERSONAL_USER_HANDLE = UserHandle.of(10)
- private val WORK_USER_HANDLE = UserHandle.of(20)
-
- private val context = InstrumentationRegistry.getInstrumentation().getContext()
- private val inflater = Supplier {
- LayoutInflater.from(context).inflate(R.layout.resolver_list_per_profile, null, false)
- as ViewGroup
- }
-
- @Test
- fun testSinglePageProfileAdapter() {
- val personalListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter),
- object : EmptyStateProvider {},
- { false },
- PROFILE_PERSONAL,
- null,
- null,
- inflater,
- { Optional.empty() }
- )
- assertThat(pagerAdapter.count).isEqualTo(1)
- assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_PERSONAL)
- assertThat(pagerAdapter.currentUserHandle).isEqualTo(PERSONAL_USER_HANDLE)
- assertThat(pagerAdapter.getAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.inactiveListAdapter).isNull()
- assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.workListAdapter).isNull()
- assertThat(pagerAdapter.itemCount).isEqualTo(1)
- // TODO: consider covering some of the package-private methods (and making them public?).
- // TODO: consider exercising responsibilities as an implementation of a ViewPager adapter.
- }
-
- @Test
- fun testTwoProfilePagerAdapter() {
- val personalListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) }
- val workListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE) }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter, workListAdapter),
- object : EmptyStateProvider {},
- { false },
- PROFILE_PERSONAL,
- WORK_USER_HANDLE, // TODO: why does this test pass even if this is null?
- null,
- inflater,
- { Optional.empty() }
- )
- assertThat(pagerAdapter.count).isEqualTo(2)
- assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_PERSONAL)
- assertThat(pagerAdapter.currentUserHandle).isEqualTo(PERSONAL_USER_HANDLE)
- assertThat(pagerAdapter.getAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.getAdapterForIndex(1)).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.inactiveListAdapter).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.workListAdapter).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.itemCount).isEqualTo(2)
- // TODO: consider covering some of the package-private methods (and making them public?).
- // TODO: consider exercising responsibilities as an implementation of a ViewPager adapter;
- // especially matching profiles to ListViews?
- // TODO: test ProfileSelectedListener (and getters for "current" state) as the selected
- // page changes. Currently there's no API to change the selected page directly; that's
- // only possible through manipulation of the bound ViewPager.
- }
-
- @Test
- fun testTwoProfilePagerAdapter_workIsDefault() {
- val personalListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) }
- val workListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE) }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter, workListAdapter),
- object : EmptyStateProvider {},
- { false },
- PROFILE_WORK, // <-- This test specifically requests we start on work profile.
- WORK_USER_HANDLE, // TODO: why does this test pass even if this is null?
- null,
- inflater,
- { Optional.empty() }
- )
- assertThat(pagerAdapter.count).isEqualTo(2)
- assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_WORK)
- assertThat(pagerAdapter.currentUserHandle).isEqualTo(WORK_USER_HANDLE)
- assertThat(pagerAdapter.getAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.getAdapterForIndex(1)).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.inactiveListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.workListAdapter).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.itemCount).isEqualTo(2)
- // TODO: consider covering some of the package-private methods (and making them public?).
- // TODO: test ProfileSelectedListener (and getters for "current" state) as the selected
- // page changes. Currently there's no API to change the selected page directly; that's
- // only possible through manipulation of the bound ViewPager.
- }
-
- @Test
- fun testBottomPaddingDelegate_default() {
- val container =
- mock<View> {
- whenever(getPaddingLeft()).thenReturn(1)
- whenever(getPaddingTop()).thenReturn(2)
- whenever(getPaddingRight()).thenReturn(3)
- whenever(getPaddingBottom()).thenReturn(4)
- }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(),
- object : EmptyStateProvider {},
- { false },
- PROFILE_PERSONAL,
- null,
- null,
- inflater,
- { Optional.empty() }
- )
- pagerAdapter.setupContainerPadding(container)
- verify(container, never()).setPadding(any(), any(), any(), any())
- }
-
- @Test
- fun testBottomPaddingDelegate_override() {
- val container =
- mock<View> {
- whenever(getPaddingLeft()).thenReturn(1)
- whenever(getPaddingTop()).thenReturn(2)
- whenever(getPaddingRight()).thenReturn(3)
- whenever(getPaddingBottom()).thenReturn(4)
- }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(),
- object : EmptyStateProvider {},
- { false },
- PROFILE_PERSONAL,
- null,
- null,
- inflater,
- { Optional.of(42) }
- )
- pagerAdapter.setupContainerPadding(container)
- verify(container).setPadding(1, 2, 3, 42)
- }
-
- @Test
- fun testPresumedQuietModeEmptyStateForWorkProfile_whenQuiet() {
- // TODO: this is "presumed" because the conditions to determine whether we "should" show an
- // empty state aren't enforced to align with the conditions when we actually *would* -- I
- // believe `shouldShowEmptyStateScreen` should be implemented in terms of the provider?
- val personalListAdapter =
- mock<ResolverListAdapter> {
- whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE)
- whenever(getUnfilteredCount()).thenReturn(1)
- }
- val workListAdapter =
- mock<ResolverListAdapter> {
- whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE)
- whenever(getUnfilteredCount()).thenReturn(1)
- }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter, workListAdapter),
- object : EmptyStateProvider {},
- { true }, // <-- Work mode is quiet.
- PROFILE_WORK,
- WORK_USER_HANDLE,
- null,
- inflater,
- { Optional.empty() }
- )
- assertThat(pagerAdapter.shouldShowEmptyStateScreen(workListAdapter)).isTrue()
- assertThat(pagerAdapter.shouldShowEmptyStateScreen(personalListAdapter)).isFalse()
- }
-
- @Test
- fun testPresumedQuietModeEmptyStateForWorkProfile_notWhenNotQuiet() {
- // TODO: this is "presumed" because the conditions to determine whether we "should" show an
- // empty state aren't enforced to align with the conditions when we actually *would* -- I
- // believe `shouldShowEmptyStateScreen` should be implemented in terms of the provider?
- val personalListAdapter =
- mock<ResolverListAdapter> {
- whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE)
- whenever(getUnfilteredCount()).thenReturn(1)
- }
- val workListAdapter =
- mock<ResolverListAdapter> {
- whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE)
- whenever(getUnfilteredCount()).thenReturn(1)
- }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter, workListAdapter),
- object : EmptyStateProvider {},
- { false }, // <-- Work mode is not quiet.
- PROFILE_WORK,
- WORK_USER_HANDLE,
- null,
- inflater,
- { Optional.empty() }
- )
- assertThat(pagerAdapter.shouldShowEmptyStateScreen(workListAdapter)).isFalse()
- assertThat(pagerAdapter.shouldShowEmptyStateScreen(personalListAdapter)).isFalse()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/ResolverActivityTest.java b/java/tests/src/com/android/intentresolver/ResolverActivityTest.java
deleted file mode 100644
index dde2f980..00000000
--- a/java/tests/src/com/android/intentresolver/ResolverActivityTest.java
+++ /dev/null
@@ -1,1097 +0,0 @@
-/*
- * Copyright (C) 2016 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 static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.action.ViewActions.swipeUp;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-
-import static com.android.intentresolver.MatcherUtils.first;
-import static com.android.intentresolver.ResolverWrapperActivity.sOverrides;
-
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.espresso.Espresso;
-import androidx.test.espresso.NoMatchingViewException;
-import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.intentresolver.widget.ResolverDrawerLayout;
-
-import com.google.android.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Resolver activity instrumentation tests
- */
-@RunWith(AndroidJUnit4.class)
-public class ResolverActivityTest {
-
- private static final UserHandle PERSONAL_USER_HANDLE = androidx.test.platform.app
- .InstrumentationRegistry.getInstrumentation().getTargetContext().getUser();
- private static final UserHandle WORK_PROFILE_USER_HANDLE = UserHandle.of(10);
- private static final UserHandle CLONE_PROFILE_USER_HANDLE = UserHandle.of(11);
-
- @Rule
- public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
- new ActivityTestRule<>(ResolverWrapperActivity.class, false, false);
-
- @Before
- public void setup() {
- // TODO: use the other form of `adoptShellPermissionIdentity()` where we explicitly list the
- // permissions we require (which we'll read from the manifest at runtime).
- androidx.test.platform.app.InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .adoptShellPermissionIdentity();
-
- sOverrides.reset();
- }
-
- @Test
- public void twoOptionsAndUserSelectsOne() throws InterruptedException {
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2,
- PERSONAL_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- assertThat(activity.getAdapter().getCount(), is(2));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
- onView(withText(toChoose.activityInfo.name))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Ignore // Failing - b/144929805
- @Test
- public void setMaxHeight() throws Exception {
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2,
- PERSONAL_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
- waitForIdle();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- final View viewPager = activity.findViewById(com.android.internal.R.id.profile_pager);
- final int initialResolverHeight = viewPager.getHeight();
-
- activity.runOnUiThread(() -> {
- ResolverDrawerLayout layout = (ResolverDrawerLayout)
- activity.findViewById(
- com.android.internal.R.id.contentPanel);
- ((ResolverDrawerLayout.LayoutParams) viewPager.getLayoutParams()).maxHeight
- = initialResolverHeight - 1;
- // Force a relayout
- layout.invalidate();
- layout.requestLayout();
- });
- waitForIdle();
- assertThat("Drawer should be capped at maxHeight",
- viewPager.getHeight() == (initialResolverHeight - 1));
-
- activity.runOnUiThread(() -> {
- ResolverDrawerLayout layout = (ResolverDrawerLayout)
- activity.findViewById(
- com.android.internal.R.id.contentPanel);
- ((ResolverDrawerLayout.LayoutParams) viewPager.getLayoutParams()).maxHeight
- = initialResolverHeight + 1;
- // Force a relayout
- layout.invalidate();
- layout.requestLayout();
- });
- waitForIdle();
- assertThat("Drawer should not change height if its height is less than maxHeight",
- viewPager.getHeight() == initialResolverHeight);
- }
-
- @Ignore // Failing - b/144929805
- @Test
- public void setShowAtTopToTrue() throws Exception {
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2,
- PERSONAL_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
- waitForIdle();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- final View viewPager = activity.findViewById(com.android.internal.R.id.profile_pager);
- final View divider = activity.findViewById(com.android.internal.R.id.divider);
- final RelativeLayout profileView =
- (RelativeLayout) activity.findViewById(com.android.internal.R.id.profile_button)
- .getParent();
- assertThat("Drawer should show at bottom by default",
- profileView.getBottom() + divider.getHeight() == viewPager.getTop()
- && profileView.getTop() > 0);
-
- activity.runOnUiThread(() -> {
- ResolverDrawerLayout layout = (ResolverDrawerLayout)
- activity.findViewById(
- com.android.internal.R.id.contentPanel);
- layout.setShowAtTop(true);
- });
- waitForIdle();
- assertThat("Drawer should show at top with new attribute",
- profileView.getBottom() + divider.getHeight() == viewPager.getTop()
- && profileView.getTop() == 0);
- }
-
- @Test
- public void hasLastChosenActivity() throws Exception {
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2,
- PERSONAL_USER_HANDLE);
- ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
-
- setupResolverControllers(resolvedComponentInfos);
- when(sOverrides.resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- // The other entry is filtered to the last used slot
- assertThat(activity.getAdapter().getCount(), is(1));
- assertThat(activity.getAdapter().getPlaceholderCount(), is(1));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- onView(withId(com.android.internal.R.id.button_once)).perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test
- public void hasOtherProfileOneOption() throws Exception {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
-
- ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
- Intent sendIntent = createSendImageIntent();
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- // The other entry is filtered to the last used slot
- assertThat(activity.getAdapter().getCount(), is(1));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10,
- PERSONAL_USER_HANDLE);
- // We pick the first one as there is another one in the work profile side
- onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test
- public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- // The other entry is filtered to the other profile slot
- assertThat(activity.getAdapter().getCount(), is(2));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- // Confirm that the button bar is disabled by default
- onView(withId(com.android.internal.R.id.button_once)).check(matches(not(isEnabled())));
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2, PERSONAL_USER_HANDLE);
-
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once)).perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
-
- @Test
- public void hasLastChosenActivityAndOtherProfile() throws Exception {
- // In this case we prefer the other profile and don't display anything about the last
- // chosen activity.
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- setupResolverControllers(resolvedComponentInfos);
- when(sOverrides.resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0));
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- // The other entry is filtered to the other profile slot
- assertThat(activity.getAdapter().getCount(), is(2));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- // Confirm that the button bar is disabled by default
- onView(withId(com.android.internal.R.id.button_once)).check(matches(not(isEnabled())));
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2, PERSONAL_USER_HANDLE);
-
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once)).perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test
- public void testWorkTab_displayedWhenWorkProfileUserAvailable() {
- Intent sendIntent = createSendImageIntent();
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.tabs)).check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_hiddenWhenWorkProfileUserNotAvailable() {
- Intent sendIntent = createSendImageIntent();
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.tabs)).check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testWorkTab_workTabListPopulatedBeforeGoingToTab() throws InterruptedException {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId = */ 10,
- PERSONAL_USER_HANDLE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos,
- new ArrayList<>(workResolvedComponentInfos));
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
- // The work list adapter must be populated in advance before tapping the other tab
- assertThat(activity.getWorkListAdapter().getCount(), is(4));
- }
-
- @Test
- public void testWorkTab_workTabUsesExpectedAdapter() {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
-
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
- assertThat(activity.getWorkListAdapter().getCount(), is(4));
- }
-
- @Test
- public void testWorkTab_personalTabUsesExpectedAdapter() {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
-
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
- assertThat(activity.getPersonalListAdapter().getCount(), is(2));
- }
-
- @Test
- public void testWorkTab_workProfileHasExpectedNumberOfTargets() throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
- waitForIdle();
- assertThat(activity.getWorkListAdapter().getCount(), is(4));
- }
-
- @Test
- public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
- waitForIdle();
- onView(first(allOf(withText(workResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
-
- waitForIdle();
- assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
- }
-
- @Test
- public void testWorkTab_noPersonalApps_workTabHasExpectedNumberOfTargets()
- throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(1, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
-
- waitForIdle();
- assertThat(activity.getWorkListAdapter().getCount(), is(4));
- }
-
- @Test
- public void testWorkTab_headerIsVisibleInPersonalTab() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(1, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createOpenWebsiteIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- TextView headerText = activity.findViewById(com.android.internal.R.id.title);
- String initialText = headerText.getText().toString();
- assertFalse("Header text is empty.", initialText.isEmpty());
- assertThat(headerText.getVisibility(), is(View.VISIBLE));
- }
-
- @Test
- public void testWorkTab_switchTabs_headerStaysSame() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(1, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createOpenWebsiteIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- TextView headerText = activity.findViewById(com.android.internal.R.id.title);
- String initialText = headerText.getText().toString();
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
-
- waitForIdle();
- String currentText = headerText.getText().toString();
- assertThat(headerText.getVisibility(), is(View.VISIBLE));
- assertThat(String.format("Header text is not the same when switching tabs, personal profile"
- + " header was %s but work profile header is %s", initialText, currentText),
- TextUtils.equals(initialText, currentText));
- }
-
- @Test
- public void testWorkTab_noPersonalApps_canStartWorkApps()
- throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId= */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
- waitForIdle();
- onView(first(allOf(
- withText(workResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name),
- isDisplayed())))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
- waitForIdle();
-
- assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
- }
-
- @Test
- public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets, WORK_PROFILE_USER_HANDLE);
- sOverrides.hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
-
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_workProfileDisabled_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets, WORK_PROFILE_USER_HANDLE);
- sOverrides.isQuietModeEnabled = true;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_turn_on_work_apps))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_noWorkAppsAvailable_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0, WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_no_work_apps_available))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0, WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
- sOverrides.isQuietModeEnabled = true;
- sOverrides.hasCrossProfileIntents = false;
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testMiniResolver() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(1, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(1, WORK_PROFILE_USER_HANDLE);
- // Personal profile only has a browser
- personalResolvedComponentInfos.get(0).getResolveInfoAt(0).handleAllWebDataURI = true;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.open_cross_profile)).check(matches(isDisplayed()));
- }
-
- @Test
- public void testMiniResolver_noCurrentProfileTarget() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(0, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(1, WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- // Need to ensure mini resolver doesn't trigger here.
- assertNotMiniResolver();
- }
-
- private void assertNotMiniResolver() {
- try {
- onView(withId(com.android.internal.R.id.open_cross_profile))
- .check(matches(isDisplayed()));
- } catch (NoMatchingViewException e) {
- return;
- }
- fail("Mini resolver present but shouldn't be");
- }
-
- @Test
- public void testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0, WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
- sOverrides.isQuietModeEnabled = true;
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_no_work_apps_available))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_onePersonalTarget_emptyStateOnWorkTarget_doesNotAutoLaunch() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets, WORK_PROFILE_USER_HANDLE);
- sOverrides.hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- assertNull(chosen[0]);
- }
-
- @Test
- public void testLayoutWithDefault_withWorkTab_neverShown() throws RemoteException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- // In this case we prefer the other profile and don't display anything about the last
- // chosen activity.
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTest(2, PERSONAL_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
- when(sOverrides.resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0));
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- // The other entry is filtered to the last used slot
- assertThat(activity.getAdapter().hasFilteredItem(), is(false));
- assertThat(activity.getAdapter().getCount(), is(2));
- assertThat(activity.getAdapter().getPlaceholderCount(), is(2));
- }
-
- @Test
- public void testClonedProfilePresent_personalAdapterIsSetWithPersonalProfile() {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ false, /* cloneAvailable= */ true);
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- setupResolverControllers(resolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- assertThat(activity.getCurrentUserHandle(), is(PERSONAL_USER_HANDLE));
- assertThat(activity.getAdapter().getCount(), is(3));
- }
-
- @Test
- public void testClonedProfilePresent_personalTabUsesExpectedAdapter() {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ true);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- assertThat(activity.getCurrentUserHandle(), is(PERSONAL_USER_HANDLE));
- assertThat(activity.getAdapter().getCount(), is(3));
- }
-
- @Test
- public void testClonedProfilePresent_layoutWithDefault_neverShown() throws Exception {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ false, /* cloneAvailable= */ true);
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 2,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
- when(sOverrides.resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- assertThat(activity.getAdapter().hasFilteredItem(), is(false));
- assertThat(activity.getAdapter().getCount(), is(2));
- assertThat(activity.getAdapter().getPlaceholderCount(), is(2));
- }
-
- @Test
- public void testClonedProfilePresent_alwaysButtonDisabled() throws Exception {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ false, /* cloneAvailable= */ true);
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
- when(sOverrides.resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- // Confirm that the button bar is disabled by default
- onView(withId(com.android.internal.R.id.button_once)).check(matches(not(isEnabled())));
- onView(withId(com.android.internal.R.id.button_always)).check(matches(not(isEnabled())));
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2, PERSONAL_USER_HANDLE);
-
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
- .perform(click());
-
- onView(withId(com.android.internal.R.id.button_once)).check(matches(isEnabled()));
- onView(withId(com.android.internal.R.id.button_always)).check(matches(not(isEnabled())));
- }
-
- @Test
- public void testClonedProfilePresent_personalProfileActivityIsStartedInCorrectUser()
- throws Exception {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ true);
-
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(3, WORK_PROFILE_USER_HANDLE);
- sOverrides.hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
- final UserHandle[] selectedActivityUserHandle = new UserHandle[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- selectedActivityUserHandle[0] = result.second;
- return true;
- };
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(first(allOf(withText(personalResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
- waitForIdle();
-
- assertThat(selectedActivityUserHandle[0], is(activity.getAdapter().getUserHandle()));
- }
-
- @Test
- public void testClonedProfilePresent_workProfileActivityIsStartedInCorrectUser()
- throws Exception {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ true);
-
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(3, WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
- final UserHandle[] selectedActivityUserHandle = new UserHandle[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- selectedActivityUserHandle[0] = result.second;
- return true;
- };
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
- waitForIdle();
- onView(first(allOf(withText(workResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
- waitForIdle();
-
- assertThat(selectedActivityUserHandle[0], is(activity.getAdapter().getUserHandle()));
- }
-
- @Test
- public void testClonedProfilePresent_personalProfileResolverComparatorHasCorrectUsers()
- throws Exception {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ false, /* cloneAvailable= */ true);
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- setupResolverControllers(resolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- List<UserHandle> result = activity
- .getResolverRankerServiceUserHandleList(PERSONAL_USER_HANDLE);
-
- assertThat(result.containsAll(
- Lists.newArrayList(PERSONAL_USER_HANDLE, CLONE_PROFILE_USER_HANDLE)), is(true));
- }
-
- private Intent createSendImageIntent() {
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
- sendIntent.setType("image/jpeg");
- return sendIntent;
- }
-
- private Intent createOpenWebsiteIntent() {
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_VIEW);
- sendIntent.setData(Uri.parse("https://google.com"));
- return sendIntent;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults,
- UserHandle resolvedForUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, resolvedForUser));
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsWithCloneProfileForTest(
- int numberOfResults,
- UserHandle resolvedForPersonalUser,
- UserHandle resolvedForClonedUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < 1; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- resolvedForPersonalUser));
- }
- for (int i = 1; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- resolvedForClonedUser));
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
- int numberOfResults,
- UserHandle resolvedForUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- if (i == 0) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i,
- resolvedForUser));
- } else {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, resolvedForUser));
- }
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
- int numberOfResults, int userId, UserHandle resolvedForUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- if (i == 0) {
- infoList.add(
- ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId,
- resolvedForUser));
- } else {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, resolvedForUser));
- }
- }
- return infoList;
- }
-
- private void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- private void markOtherProfileAvailability(boolean workAvailable, boolean cloneAvailable) {
- AnnotatedUserHandles.Builder handles = AnnotatedUserHandles.newBuilder();
- handles
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(PERSONAL_USER_HANDLE)
- .setPersonalProfileUserHandle(PERSONAL_USER_HANDLE);
- if (workAvailable) {
- handles.setWorkProfileUserHandle(WORK_PROFILE_USER_HANDLE);
- }
- if (cloneAvailable) {
- handles.setCloneProfileUserHandle(CLONE_PROFILE_USER_HANDLE);
- }
- sOverrides.annotatedUserHandles = handles.build();
- }
-
- private void setupResolverControllers(
- List<ResolvedComponentInfo> personalResolvedComponentInfos) {
- setupResolverControllers(personalResolvedComponentInfos, new ArrayList<>());
- }
-
- private void setupResolverControllers(
- List<ResolvedComponentInfo> personalResolvedComponentInfos,
- List<ResolvedComponentInfo> workResolvedComponentInfos) {
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .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
deleted file mode 100644
index db109941..00000000
--- a/java/tests/src/com/android/intentresolver/ResolverDataProvider.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2008 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.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.os.UserHandle;
-import android.test.mock.MockContext;
-import android.test.mock.MockPackageManager;
-import android.test.mock.MockResources;
-
-import androidx.annotation.NonNull;
-
-/**
- * Utility class used by resolver tests to create mock data
- */
-public class ResolverDataProvider {
-
- static private int USER_SOMEONE_ELSE = 10;
-
- static ResolvedComponentInfo createResolvedComponentInfo(int i) {
- return new ResolvedComponentInfo(
- createComponentName(i),
- createResolverIntent(i),
- createResolveInfo(i, UserHandle.USER_CURRENT));
- }
-
- public static ResolvedComponentInfo createResolvedComponentInfo(int i,
- UserHandle resolvedForUser) {
- return new ResolvedComponentInfo(
- createComponentName(i),
- createResolverIntent(i),
- createResolveInfo(i, UserHandle.USER_CURRENT, resolvedForUser));
- }
-
- static ResolvedComponentInfo createResolvedComponentInfo(
- ComponentName componentName, Intent intent) {
- return new ResolvedComponentInfo(
- componentName,
- intent,
- createResolveInfo(componentName, UserHandle.USER_CURRENT));
- }
-
- public static ResolvedComponentInfo createResolvedComponentInfo(
- ComponentName componentName, Intent intent, UserHandle resolvedForUser) {
- return new ResolvedComponentInfo(
- componentName,
- intent,
- createResolveInfo(componentName, UserHandle.USER_CURRENT, resolvedForUser));
- }
-
- static ResolvedComponentInfo createResolvedComponentInfoWithOtherId(int i) {
- return new ResolvedComponentInfo(
- createComponentName(i),
- createResolverIntent(i),
- createResolveInfo(i, USER_SOMEONE_ELSE));
- }
-
- public static ResolvedComponentInfo createResolvedComponentInfoWithOtherId(int i,
- UserHandle resolvedForUser) {
- return new ResolvedComponentInfo(
- createComponentName(i),
- createResolverIntent(i),
- createResolveInfo(i, USER_SOMEONE_ELSE, resolvedForUser));
- }
-
- static ResolvedComponentInfo createResolvedComponentInfoWithOtherId(int i, int userId) {
- return new ResolvedComponentInfo(
- createComponentName(i),
- createResolverIntent(i),
- createResolveInfo(i, userId));
- }
-
- public static ResolvedComponentInfo createResolvedComponentInfoWithOtherId(int i,
- int userId, UserHandle resolvedForUser) {
- return new ResolvedComponentInfo(
- createComponentName(i),
- createResolverIntent(i),
- createResolveInfo(i, userId, resolvedForUser));
- }
-
- public static ComponentName createComponentName(int i) {
- final String name = "component" + i;
- return new ComponentName("foo.bar." + name, name);
- }
-
- public static ResolveInfo createResolveInfo(int i, int userId) {
- return createResolveInfo(i, userId, UserHandle.of(userId));
- }
-
- public static ResolveInfo createResolveInfo(int i, int userId, UserHandle resolvedForUser) {
- return createResolveInfo(createActivityInfo(i), userId, resolvedForUser);
- }
-
- public static ResolveInfo createResolveInfo(ComponentName componentName, int userId) {
- return createResolveInfo(componentName, userId, UserHandle.of(userId));
- }
-
- public static ResolveInfo createResolveInfo(
- ComponentName componentName, int userId, UserHandle resolvedForUser) {
- return createResolveInfo(createActivityInfo(componentName), userId, resolvedForUser);
- }
-
- public static ResolveInfo createResolveInfo(
- ActivityInfo activityInfo, int userId, UserHandle resolvedForUser) {
- final ResolveInfo resolveInfo = new ResolveInfo();
- resolveInfo.activityInfo = activityInfo;
- resolveInfo.targetUserId = userId;
- resolveInfo.userHandle = resolvedForUser;
- return resolveInfo;
- }
-
- static ActivityInfo createActivityInfo(int i) {
- ActivityInfo ai = new ActivityInfo();
- ai.name = "activity_name" + i;
- ai.packageName = "foo_bar" + i;
- ai.enabled = true;
- ai.exported = true;
- ai.permission = null;
- ai.applicationInfo = createApplicationInfo();
- 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";
- ai.packageName = "foo.bar";
- ai.enabled = true;
- return ai;
- }
-
- static class PackageManagerMockedInfo {
- public Context ctx;
- public ApplicationInfo appInfo;
- public ActivityInfo activityInfo;
- public ResolveInfo resolveInfo;
- public String setAppLabel;
- public String setActivityLabel;
- public String setResolveInfoLabel;
- }
-
- /** Create a {@link PackageManagerMockedInfo} with all distinct labels. */
- static PackageManagerMockedInfo createPackageManagerMockedInfo(boolean hasOverridePermission) {
- return createPackageManagerMockedInfo(
- hasOverridePermission, "app_label", "activity_label", "resolve_info_label");
- }
-
- static PackageManagerMockedInfo createPackageManagerMockedInfo(
- boolean hasOverridePermission,
- String appLabel,
- String activityLabel,
- String resolveInfoLabel) {
- MockContext ctx = new MockContext() {
- @Override
- public PackageManager getPackageManager() {
- return new MockPackageManager() {
- @Override
- public int checkPermission(String permName, String pkgName) {
- if (hasOverridePermission) return PERMISSION_GRANTED;
- return PERMISSION_DENIED;
- }
- };
- }
-
- @Override
- public Resources getResources() {
- return new MockResources() {
- @NonNull
- @Override
- public String getString(int id) throws NotFoundException {
- if (id == 1) return appLabel;
- if (id == 2) return activityLabel;
- if (id == 3) return resolveInfoLabel;
- throw new NotFoundException();
- }
- };
- }
- };
-
- ApplicationInfo appInfo = new ApplicationInfo() {
- @NonNull
- @Override
- public CharSequence loadLabel(@NonNull PackageManager pm) {
- return appLabel;
- }
- };
- appInfo.labelRes = 1;
-
- ActivityInfo activityInfo = new ActivityInfo() {
- @NonNull
- @Override
- public CharSequence loadLabel(@NonNull PackageManager pm) {
- return activityLabel;
- }
- };
- activityInfo.labelRes = 2;
- activityInfo.applicationInfo = appInfo;
-
- ResolveInfo resolveInfo = new ResolveInfo() {
- @NonNull
- @Override
- public CharSequence loadLabel(@NonNull PackageManager pm) {
- return resolveInfoLabel;
- }
- };
- resolveInfo.activityInfo = activityInfo;
- resolveInfo.resolvePackageName = "super.fake.packagename";
- resolveInfo.labelRes = 3;
-
- PackageManagerMockedInfo mockedInfo = new PackageManagerMockedInfo();
- mockedInfo.activityInfo = activityInfo;
- mockedInfo.appInfo = appInfo;
- mockedInfo.ctx = ctx;
- mockedInfo.resolveInfo = resolveInfo;
- mockedInfo.setAppLabel = appLabel;
- mockedInfo.setActivityLabel = activityLabel;
- mockedInfo.setResolveInfoLabel = resolveInfoLabel;
-
- return mockedInfo;
- }
-
- static Intent createResolverIntent(int i) {
- return new Intent("intentAction" + i);
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/ResolverListAdapterTest.kt b/java/tests/src/com/android/intentresolver/ResolverListAdapterTest.kt
deleted file mode 100644
index 61b9fd9c..00000000
--- a/java/tests/src/com/android/intentresolver/ResolverListAdapterTest.kt
+++ /dev/null
@@ -1,1048 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver
-
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.content.pm.ResolveInfo
-import android.os.UserHandle
-import android.os.UserManager
-import android.view.LayoutInflater
-import com.android.intentresolver.ResolverDataProvider.createActivityInfo
-import com.android.intentresolver.ResolverListAdapter.ResolverListCommunicator
-import com.android.intentresolver.icons.TargetDataLoader
-import com.android.intentresolver.util.TestExecutor
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.inOrder
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-
-private const val PKG_NAME = "org.pkg.app"
-private const val PKG_NAME_TWO = "org.pkg.two.app"
-private const val PKG_NAME_THREE = "org.pkg.three.app"
-private const val CLASS_NAME = "org.pkg.app.TheClass"
-
-class ResolverListAdapterTest {
- private val layoutInflater = mock<LayoutInflater>()
- private val packageManager = mock<PackageManager>()
- private val userManager = mock<UserManager> { whenever(isManagedProfile).thenReturn(false) }
- private val context =
- mock<Context> {
- whenever(getSystemService(Context.LAYOUT_INFLATER_SERVICE)).thenReturn(layoutInflater)
- whenever(getSystemService(Context.USER_SERVICE)).thenReturn(userManager)
- whenever(packageManager).thenReturn(this@ResolverListAdapterTest.packageManager)
- }
- private val targetIntent = Intent(Intent.ACTION_SEND)
- private val payloadIntents = listOf(targetIntent)
- private val resolverListController =
- mock<ResolverListController> {
- whenever(filterIneligibleActivities(any(), anyBoolean())).thenReturn(null)
- whenever(filterLowPriority(any(), anyBoolean())).thenReturn(null)
- }
- private val resolverListCommunicator = FakeResolverListCommunicator()
- private val userHandle = UserHandle.of(UserHandle.USER_CURRENT)
- private val targetDataLoader = mock<TargetDataLoader>()
- private val backgroundExecutor = TestExecutor()
- private val immediateExecutor = TestExecutor(immediate = true)
-
- @Test
- fun test_oneTargetNoLastChosen_oneTargetInAdapter() {
- val resolvedTargets = createResolvedComponents(ComponentName(PKG_NAME, CLASS_NAME))
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- val isLoaded = testSubject.rebuildList(doPostProcessing)
-
- assertThat(isLoaded).isTrue()
- assertThat(testSubject.count).isEqualTo(resolvedTargets.size)
- assertThat(testSubject.placeholderCount).isEqualTo(0)
- assertThat(testSubject.hasFilteredItem()).isFalse()
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isTrue()
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(0)
- assertThat(resolverListCommunicator.updateProfileViewButtonCount).isEqualTo(0)
- assertThat(resolverListCommunicator.sendVoiceCommandCount).isEqualTo(1)
- }
-
- @Test
- fun test_oneTargetThatWasLastChosen_NoTargetsInAdapter() {
- val resolvedTargets = createResolvedComponents(ComponentName(PKG_NAME, CLASS_NAME))
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- whenever(resolverListController.lastChosen)
- .thenReturn(resolvedTargets[0].getResolveInfoAt(0))
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- val isLoaded = testSubject.rebuildList(doPostProcessing)
-
- assertThat(isLoaded).isTrue()
- assertThat(testSubject.count).isEqualTo(0)
- assertThat(testSubject.placeholderCount).isEqualTo(0)
- assertThat(testSubject.hasFilteredItem()).isTrue()
- assertThat(testSubject.filteredItem).isNotNull()
- assertThat(testSubject.filteredPosition).isEqualTo(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isTrue()
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(0)
- }
-
- @Test
- fun test_oneTargetLastChosenNotInTheList_oneTargetInAdapter() {
- val resolvedTargets = createResolvedComponents(ComponentName(PKG_NAME, CLASS_NAME))
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- whenever(resolverListController.lastChosen)
- .thenReturn(createResolveInfo(PKG_NAME_TWO, CLASS_NAME))
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- val isLoaded = testSubject.rebuildList(doPostProcessing)
-
- assertThat(isLoaded).isTrue()
- assertThat(testSubject.count).isEqualTo(resolvedTargets.size)
- assertThat(testSubject.placeholderCount).isEqualTo(0)
- assertThat(testSubject.hasFilteredItem()).isTrue()
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isTrue()
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(0)
- }
-
- @Test
- fun test_oneTargetThatWasLastChosenFilteringDisabled_oneTargetInAdapter() {
- val resolvedTargets = createResolvedComponents(ComponentName(PKG_NAME, CLASS_NAME))
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- whenever(resolverListController.lastChosen)
- .thenReturn(resolvedTargets[0].getResolveInfoAt(0))
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ false,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- val isLoaded = testSubject.rebuildList(doPostProcessing)
-
- assertThat(isLoaded).isTrue()
- assertThat(testSubject.count).isEqualTo(resolvedTargets.size)
- // we don't reset placeholder count
- assertThat(testSubject.placeholderCount).isEqualTo(0)
- assertThat(testSubject.hasFilteredItem()).isFalse()
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isTrue()
- }
-
- @Test
- fun test_twoTargetsNoLastChosenUseLayoutWithDefaults_twoTargetsInAdapter() {
- testTwoTargets(hasLastChosen = false, useLayoutWithDefaults = true)
- }
-
- @Test
- fun test_twoTargetsNoLastChosenDontUseLayoutWithDefaults_twoTargetsInAdapter() {
- testTwoTargets(hasLastChosen = false, useLayoutWithDefaults = false)
- }
-
- @Test
- fun test_twoTargetsLastChosenUseLayoutWithDefaults_oneTargetInAdapter() {
- testTwoTargets(hasLastChosen = true, useLayoutWithDefaults = true)
- }
-
- @Test
- fun test_twoTargetsLastChosenDontUseLayoutWithDefaults_oneTargetInAdapter() {
- testTwoTargets(hasLastChosen = true, useLayoutWithDefaults = false)
- }
-
- private fun testTwoTargets(hasLastChosen: Boolean, useLayoutWithDefaults: Boolean) {
- val resolvedTargets =
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- if (hasLastChosen) {
- whenever(resolverListController.lastChosen)
- .thenReturn(resolvedTargets[0].getResolveInfoAt(0))
- }
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- val resolverListCommunicator = FakeResolverListCommunicator(useLayoutWithDefaults)
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- val isLoaded = testSubject.rebuildList(doPostProcessing)
-
- assertThat(isLoaded).isFalse()
- val placeholderCount = resolvedTargets.size - (if (useLayoutWithDefaults) 1 else 0)
- assertThat(testSubject.count).isEqualTo(placeholderCount)
- assertThat(testSubject.placeholderCount).isEqualTo(placeholderCount)
- assertThat(testSubject.hasFilteredItem()).isEqualTo(hasLastChosen)
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isFalse()
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(1)
- assertThat(resolverListCommunicator.updateProfileViewButtonCount).isEqualTo(0)
- assertThat(resolverListCommunicator.sendVoiceCommandCount).isEqualTo(0)
-
- backgroundExecutor.runUntilIdle()
-
- // we don't reset placeholder count (legacy logic, likely an oversight?)
- assertThat(testSubject.placeholderCount).isEqualTo(placeholderCount)
- assertThat(testSubject.hasFilteredItem()).isEqualTo(hasLastChosen)
- if (hasLastChosen) {
- assertThat(testSubject.count).isEqualTo(resolvedTargets.size - 1)
- assertThat(testSubject.filteredItem).isNotNull()
- assertThat(testSubject.filteredPosition).isEqualTo(0)
- } else {
- assertThat(testSubject.count).isEqualTo(resolvedTargets.size)
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- }
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isTrue()
- assertThat(resolverListCommunicator.updateProfileViewButtonCount).isEqualTo(1)
- assertThat(resolverListCommunicator.sendVoiceCommandCount).isEqualTo(1)
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(0)
- }
-
- @Test
- fun test_twoTargetsLastChosenNotInTheList_twoTargetsInAdapter() {
- val resolvedTargets =
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- whenever(resolverListController.lastChosen)
- .thenReturn(createResolveInfo(PKG_NAME, CLASS_NAME + "2"))
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = false
-
- val isLoaded = testSubject.rebuildList(doPostProcessing)
-
- assertThat(isLoaded).isFalse()
- val placeholderCount = resolvedTargets.size - 1
- assertThat(testSubject.count).isEqualTo(placeholderCount)
- assertThat(testSubject.placeholderCount).isEqualTo(placeholderCount)
- assertThat(testSubject.hasFilteredItem()).isTrue()
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isFalse()
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(1)
- assertThat(resolverListCommunicator.updateProfileViewButtonCount).isEqualTo(0)
-
- backgroundExecutor.runUntilIdle()
-
- // we don't reset placeholder count (legacy logic, likely an oversight?)
- assertThat(testSubject.placeholderCount).isEqualTo(placeholderCount)
- assertThat(testSubject.hasFilteredItem()).isTrue()
- assertThat(testSubject.count).isEqualTo(resolvedTargets.size)
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isTrue()
- assertThat(resolverListCommunicator.updateProfileViewButtonCount).isEqualTo(0)
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(0)
- }
-
- @Test
- fun test_twoTargetsWithOtherProfileAndLastChosen_oneTargetInAdapter() {
- val resolvedTargets =
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- resolvedTargets[1].getResolveInfoAt(0).targetUserId = 10
- whenever(resolvedTargets[1].getResolveInfoAt(0).loadLabel(any())).thenReturn("Label")
- whenever(resolverListController.lastChosen)
- .thenReturn(resolvedTargets[0].getResolveInfoAt(0))
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- val isLoaded = testSubject.rebuildList(doPostProcessing)
-
- assertThat(isLoaded).isTrue()
- assertThat(testSubject.count).isEqualTo(1)
- assertThat(testSubject.placeholderCount).isEqualTo(0)
- assertThat(testSubject.otherProfile).isNotNull()
- assertThat(testSubject.hasFilteredItem()).isFalse()
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isTrue()
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(0)
- }
-
- @Suppress("UNCHECKED_CAST")
- @Test
- fun test_resultsSorted_appearInSortedOrderInAdapter() {
- val resolvedTargets =
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- whenever(resolverListController.sort(any())).thenAnswer { invocation ->
- val components = invocation.arguments[0] as MutableList<ResolvedComponentInfo>
- components[0] = components[1].also { components[1] = components[0] }
- null
- }
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- testSubject.rebuildList(doPostProcessing)
-
- backgroundExecutor.runUntilIdle()
-
- // we don't reset placeholder count (legacy logic, likely an oversight?)
- assertThat(testSubject.count).isEqualTo(resolvedTargets.size)
- assertThat(resolvedTargets[0].getResolveInfoAt(0).activityInfo.packageName)
- .isEqualTo(PKG_NAME_TWO)
- assertThat(resolvedTargets[1].getResolveInfoAt(0).activityInfo.packageName)
- .isEqualTo(PKG_NAME)
- }
-
- @Suppress("UNCHECKED_CAST")
- @Test
- fun test_ineligibleActivityFilteredOut_filteredComponentNotPresentInAdapter() {
- val resolvedTargets =
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- whenever(resolverListController.filterIneligibleActivities(any(), anyBoolean()))
- .thenAnswer { invocation ->
- val components = invocation.arguments[0] as MutableList<ResolvedComponentInfo>
- val original = ArrayList(components)
- components.removeAt(1)
- original
- }
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- testSubject.rebuildList(doPostProcessing)
-
- backgroundExecutor.runUntilIdle()
-
- // we don't reset placeholder count (legacy logic, likely an oversight?)
- assertThat(testSubject.count).isEqualTo(1)
- assertThat(testSubject.getItem(0)?.resolveInfo)
- .isEqualTo(resolvedTargets[0].getResolveInfoAt(0))
- assertThat(testSubject.unfilteredResolveList).hasSize(2)
- }
-
- @Suppress("UNCHECKED_CAST")
- @Test
- fun test_baseResolveList_excludedFromIneligibleActivityFiltering() {
- val rList = listOf(createResolveInfo(PKG_NAME, CLASS_NAME))
- whenever(resolverListController.addResolveListDedupe(any(), eq(targetIntent), eq(rList)))
- .thenAnswer { invocation ->
- val result = invocation.arguments[0] as MutableList<ResolvedComponentInfo>
- result.addAll(
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- )
- null
- }
- whenever(resolverListController.filterIneligibleActivities(any(), anyBoolean()))
- .thenAnswer { invocation ->
- val components = invocation.arguments[0] as MutableList<ResolvedComponentInfo>
- val original = ArrayList(components)
- components.clear()
- original
- }
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- rList,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- testSubject.rebuildList(doPostProcessing)
-
- backgroundExecutor.runUntilIdle()
-
- // we don't reset placeholder count (legacy logic, likely an oversight?)
- assertThat(testSubject.count).isEqualTo(2)
- assertThat(testSubject.unfilteredResolveList).hasSize(2)
- }
-
- @Suppress("UNCHECKED_CAST")
- @Test
- fun test_lowPriorityComponentFilteredOut_filteredComponentNotPresentInAdapter() {
- val resolvedTargets =
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- whenever(resolverListController.filterLowPriority(any(), anyBoolean())).thenAnswer {
- invocation ->
- val components = invocation.arguments[0] as MutableList<ResolvedComponentInfo>
- val original = ArrayList(components)
- components.removeAt(1)
- original
- }
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- testSubject.rebuildList(doPostProcessing)
-
- backgroundExecutor.runUntilIdle()
-
- // we don't reset placeholder count (legacy logic, likely an oversight?)
- assertThat(testSubject.count).isEqualTo(1)
- assertThat(testSubject.getItem(0)?.resolveInfo)
- .isEqualTo(resolvedTargets[0].getResolveInfoAt(0))
- assertThat(testSubject.unfilteredResolveList).hasSize(2)
- }
-
- @Test
- fun test_twoTargetsWithNonOverlappingInitialIntent_threeTargetsInAdapter() {
- val resolvedTargets =
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- val initialComponent = ComponentName(PKG_NAME_THREE, CLASS_NAME)
- val initialIntents =
- arrayOf(Intent(Intent.ACTION_SEND).apply { component = initialComponent })
- whenever(packageManager.getActivityInfo(eq(initialComponent), eq(0)))
- .thenReturn(createActivityInfo(initialComponent))
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- initialIntents,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- val isLoaded = testSubject.rebuildList(doPostProcessing)
-
- assertThat(isLoaded).isFalse()
- val placeholderCount = resolvedTargets.size - 1
- assertThat(testSubject.count).isEqualTo(placeholderCount)
- assertThat(testSubject.placeholderCount).isEqualTo(placeholderCount)
- assertThat(testSubject.hasFilteredItem()).isFalse()
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isFalse()
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(1)
- assertThat(resolverListCommunicator.updateProfileViewButtonCount).isEqualTo(0)
- assertThat(resolverListCommunicator.sendVoiceCommandCount).isEqualTo(0)
-
- backgroundExecutor.runUntilIdle()
-
- // we don't reset placeholder count (legacy logic, likely an oversight?)
- assertThat(testSubject.placeholderCount).isEqualTo(placeholderCount)
- assertThat(testSubject.hasFilteredItem()).isFalse()
- assertThat(testSubject.count).isEqualTo(resolvedTargets.size + initialIntents.size)
- assertThat(testSubject.getItem(0)?.targetIntent?.component)
- .isEqualTo(initialIntents[0].component)
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isTrue()
- assertThat(resolverListCommunicator.updateProfileViewButtonCount).isEqualTo(1)
- assertThat(resolverListCommunicator.sendVoiceCommandCount).isEqualTo(1)
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(0)
- }
-
- @Test
- fun test_twoTargetsWithOverlappingInitialIntent_twoTargetsInAdapter() {
- val resolvedTargets =
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- whenever(
- resolverListController.getResolversForIntentAsUser(
- true,
- resolverListCommunicator.shouldGetActivityMetadata(),
- resolverListCommunicator.shouldGetOnlyDefaultActivities(),
- payloadIntents,
- userHandle
- )
- )
- .thenReturn(resolvedTargets)
- val initialComponent = ComponentName(PKG_NAME_TWO, CLASS_NAME)
- val initialIntents =
- arrayOf(Intent(Intent.ACTION_SEND).apply { component = initialComponent })
- whenever(packageManager.getActivityInfo(eq(initialComponent), eq(0)))
- .thenReturn(createActivityInfo(initialComponent))
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- initialIntents,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- resolverListCommunicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = true
-
- val isLoaded = testSubject.rebuildList(doPostProcessing)
-
- assertThat(isLoaded).isFalse()
- val placeholderCount = resolvedTargets.size - 1
- assertThat(testSubject.count).isEqualTo(placeholderCount)
- assertThat(testSubject.placeholderCount).isEqualTo(placeholderCount)
- assertThat(testSubject.hasFilteredItem()).isFalse()
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isFalse()
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(1)
- assertThat(resolverListCommunicator.updateProfileViewButtonCount).isEqualTo(0)
- assertThat(resolverListCommunicator.sendVoiceCommandCount).isEqualTo(0)
-
- backgroundExecutor.runUntilIdle()
-
- // we don't reset placeholder count (legacy logic, likely an oversight?)
- assertThat(testSubject.placeholderCount).isEqualTo(placeholderCount)
- assertThat(testSubject.hasFilteredItem()).isFalse()
- assertThat(testSubject.count).isEqualTo(resolvedTargets.size)
- assertThat(testSubject.getItem(0)?.targetIntent?.component)
- .isEqualTo(initialIntents[0].component)
- assertThat(testSubject.filteredItem).isNull()
- assertThat(testSubject.filteredPosition).isLessThan(0)
- assertThat(testSubject.unfilteredResolveList).containsExactlyElementsIn(resolvedTargets)
- assertThat(testSubject.isTabLoaded).isTrue()
- assertThat(resolverListCommunicator.updateProfileViewButtonCount).isEqualTo(1)
- assertThat(resolverListCommunicator.sendVoiceCommandCount).isEqualTo(1)
- assertThat(backgroundExecutor.pendingCommandCount).isEqualTo(0)
- }
-
- @Test
- fun testPostListReadyAtEndOfRebuild_synchronous() {
- val communicator = mock<ResolverListCommunicator> {}
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- communicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = false
-
- testSubject.rebuildList(doPostProcessing)
-
- verify(communicator).onPostListReady(testSubject, doPostProcessing, true)
- }
-
- @Test
- fun testPostListReadyAtEndOfRebuild_stages() {
- // We need at least two targets to trigger asynchronous sorting/"staged" progress callbacks.
- val resolvedTargets =
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- // TODO: there's a lot of boilerplate required for this test even to trigger the expected
- // conditions; if the configuration is incorrect, the test may accidentally pass for the
- // wrong reasons. Separating responsibilities to other components will help minimize the
- // *amount* of boilerplate, but we should also consider setting up test defaults that work
- // according to our usual expectations so that we don't overlook false-negative results.
- whenever(
- resolverListController.getResolversForIntentAsUser(
- any(),
- any(),
- any(),
- any(),
- any(),
- )
- )
- .thenReturn(resolvedTargets)
- val communicator =
- mock<ResolverListCommunicator> {
- whenever(getReplacementIntent(any(), any())).thenAnswer { invocation ->
- invocation.arguments[1]
- }
- }
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- communicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- immediateExecutor,
- )
- val doPostProcessing = false
-
- testSubject.rebuildList(doPostProcessing)
-
- backgroundExecutor.runUntilIdle()
-
- val inOrder = inOrder(communicator)
- inOrder.verify(communicator).onPostListReady(testSubject, doPostProcessing, false)
- inOrder.verify(communicator).onPostListReady(testSubject, doPostProcessing, true)
- }
-
- @Test
- fun testPostListReadyAtEndOfRebuild_queued() {
- val queuedCallbacksExecutor = TestExecutor()
-
- // We need at least two targets to trigger asynchronous sorting/"staged" progress callbacks.
- val resolvedTargets =
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- // TODO: there's a lot of boilerplate required for this test even to trigger the expected
- // conditions; if the configuration is incorrect, the test may accidentally pass for the
- // wrong reasons. Separating responsibilities to other components will help minimize the
- // *amount* of boilerplate, but we should also consider setting up test defaults that work
- // according to our usual expectations so that we don't overlook false-negative results.
- whenever(
- resolverListController.getResolversForIntentAsUser(
- any(),
- any(),
- any(),
- any(),
- any(),
- )
- )
- .thenReturn(resolvedTargets)
- val communicator =
- mock<ResolverListCommunicator> {
- whenever(getReplacementIntent(any(), any())).thenAnswer { invocation ->
- invocation.arguments[1]
- }
- }
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- communicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- queuedCallbacksExecutor
- )
- val doPostProcessing = false
- testSubject.rebuildList(doPostProcessing)
-
- // Finish all the background work (enqueueing both the "partial" and "complete" progress
- // callbacks) before dequeueing either callback.
- backgroundExecutor.runUntilIdle()
- queuedCallbacksExecutor.runUntilIdle()
-
- // TODO: we may not necessarily care to assert that there's a "partial progress" callback in
- // this case, since there won't be a chance to reflect the "partial" state in the UI before
- // the "completion" is queued (and if we depend on seeing an intermediate state, that could
- // be a bad sign for our handling in the "synchronous" case?). But we should probably at
- // least assert that the "partial" callback never arrives *after* the completion?
- val inOrder = inOrder(communicator)
- inOrder.verify(communicator).onPostListReady(testSubject, doPostProcessing, false)
- inOrder.verify(communicator).onPostListReady(testSubject, doPostProcessing, true)
- }
-
- @Test
- fun testPostListReadyAtEndOfRebuild_skippedIfStillQueuedOnDestroy() {
- val queuedCallbacksExecutor = TestExecutor()
-
- // We need at least two targets to trigger asynchronous sorting/"staged" progress callbacks.
- val resolvedTargets =
- createResolvedComponents(
- ComponentName(PKG_NAME, CLASS_NAME),
- ComponentName(PKG_NAME_TWO, CLASS_NAME),
- )
- // TODO: there's a lot of boilerplate required for this test even to trigger the expected
- // conditions; if the configuration is incorrect, the test may accidentally pass for the
- // wrong reasons. Separating responsibilities to other components will help minimize the
- // *amount* of boilerplate, but we should also consider setting up test defaults that work
- // according to our usual expectations so that we don't overlook false-negative results.
- whenever(
- resolverListController.getResolversForIntentAsUser(
- any(),
- any(),
- any(),
- any(),
- any(),
- )
- )
- .thenReturn(resolvedTargets)
- val communicator =
- mock<ResolverListCommunicator> {
- whenever(getReplacementIntent(any(), any())).thenAnswer { invocation ->
- invocation.arguments[1]
- }
- }
- val testSubject =
- ResolverListAdapter(
- context,
- payloadIntents,
- /*initialIntents=*/ null,
- /*rList=*/ null,
- /*filterLastUsed=*/ true,
- resolverListController,
- userHandle,
- targetIntent,
- communicator,
- /*initialIntentsUserSpace=*/ userHandle,
- targetDataLoader,
- backgroundExecutor,
- queuedCallbacksExecutor
- )
- val doPostProcessing = false
- testSubject.rebuildList(doPostProcessing)
-
- // Finish all the background work (enqueueing both the "partial" and "complete" progress
- // callbacks) before dequeueing either callback.
- backgroundExecutor.runUntilIdle()
-
- // Notify that our activity is being destroyed while the callbacks are still queued.
- testSubject.onDestroy()
-
- queuedCallbacksExecutor.runUntilIdle()
-
- verify(communicator, never()).onPostListReady(eq(testSubject), eq(doPostProcessing), any())
- }
-
- private fun createResolvedComponents(
- vararg components: ComponentName
- ): List<ResolvedComponentInfo> {
- val result = ArrayList<ResolvedComponentInfo>(components.size)
- for (component in components) {
- val resolvedComponentInfo =
- ResolvedComponentInfo(
- ComponentName(PKG_NAME, CLASS_NAME),
- targetIntent,
- createResolveInfo(component.packageName, component.className)
- )
- result.add(resolvedComponentInfo)
- }
- return result
- }
-
- private fun createResolveInfo(packageName: String, className: String): ResolveInfo =
- mock<ResolveInfo> {
- activityInfo = createActivityInfo(ComponentName(packageName, className))
- targetUserId = this@ResolverListAdapterTest.userHandle.identifier
- userHandle = this@ResolverListAdapterTest.userHandle
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java b/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java
deleted file mode 100644
index d1adfba9..00000000
--- a/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2017 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 static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.espresso.idling.CountingIdlingResource;
-
-import com.android.intentresolver.chooser.DisplayResolveInfo;
-import com.android.intentresolver.chooser.SelectableTargetInfo;
-import com.android.intentresolver.chooser.TargetInfo;
-import com.android.intentresolver.emptystate.CrossProfileIntentsChecker;
-import com.android.intentresolver.icons.LabelInfo;
-import com.android.intentresolver.icons.TargetDataLoader;
-
-import java.util.List;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-/*
- * Simple wrapper around chooser activity to be able to initiate it under test
- */
-public class ResolverWrapperActivity extends ResolverActivity {
- static final OverrideData sOverrides = new OverrideData();
-
- private final CountingIdlingResource mLabelIdlingResource =
- new CountingIdlingResource("LoadLabelTask");
-
- public ResolverWrapperActivity() {
- super(/* isIntentPicker= */ true);
- }
-
- public CountingIdlingResource getLabelIdlingResource() {
- return mLabelIdlingResource;
- }
-
- @Override
- public ResolverListAdapter createResolverListAdapter(
- Context context,
- List<Intent> payloadIntents,
- Intent[] initialIntents,
- List<ResolveInfo> rList,
- boolean filterLastUsed,
- UserHandle userHandle,
- TargetDataLoader targetDataLoader) {
- return new ResolverListAdapter(
- context,
- payloadIntents,
- initialIntents,
- rList,
- filterLastUsed,
- createListController(userHandle),
- userHandle,
- payloadIntents.get(0), // TODO: extract upstream
- this,
- userHandle,
- new TargetDataLoaderWrapper(targetDataLoader, mLabelIdlingResource));
- }
-
- @Override
- protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
- if (sOverrides.mCrossProfileIntentsChecker != null) {
- return sOverrides.mCrossProfileIntentsChecker;
- }
- return super.createCrossProfileIntentsChecker();
- }
-
- @Override
- protected WorkProfileAvailabilityManager createWorkProfileAvailabilityManager() {
- if (sOverrides.mWorkProfileAvailability != null) {
- return sOverrides.mWorkProfileAvailability;
- }
- return super.createWorkProfileAvailabilityManager();
- }
-
- ResolverListAdapter getAdapter() {
- return mMultiProfilePagerAdapter.getActiveListAdapter();
- }
-
- ResolverListAdapter getPersonalListAdapter() {
- return ((ResolverListAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(0));
- }
-
- ResolverListAdapter getWorkListAdapter() {
- if (mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
- return null;
- }
- return ((ResolverListAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(1));
- }
-
- @Override
- public boolean isVoiceInteraction() {
- if (sOverrides.isVoiceInteraction != null) {
- return sOverrides.isVoiceInteraction;
- }
- return super.isVoiceInteraction();
- }
-
- @Override
- public void safelyStartActivityInternal(TargetInfo cti, UserHandle user,
- @Nullable Bundle options) {
- if (sOverrides.onSafelyStartInternalCallback != null
- && sOverrides.onSafelyStartInternalCallback.apply(new Pair<>(cti, user))) {
- return;
- }
- super.safelyStartActivityInternal(cti, user, options);
- }
-
- @Override
- protected ResolverListController createListController(UserHandle userHandle) {
- if (userHandle == UserHandle.SYSTEM) {
- return sOverrides.resolverListController;
- }
- return sOverrides.workResolverListController;
- }
-
- @Override
- public PackageManager getPackageManager() {
- if (sOverrides.createPackageManager != null) {
- return sOverrides.createPackageManager.apply(super.getPackageManager());
- }
- return super.getPackageManager();
- }
-
- protected UserHandle getCurrentUserHandle() {
- return mMultiProfilePagerAdapter.getCurrentUserHandle();
- }
-
- @Override
- protected AnnotatedUserHandles computeAnnotatedUserHandles() {
- return sOverrides.annotatedUserHandles;
- }
- @Override
- public void startActivityAsUser(
- @NonNull Intent intent,
- Bundle options,
- @NonNull UserHandle user
- ) {
- super.startActivityAsUser(intent, options, user);
- }
-
- @Override
- protected List<UserHandle> getResolverRankerServiceUserHandleListInternal(UserHandle
- userHandle) {
- return super.getResolverRankerServiceUserHandleListInternal(userHandle);
- }
-
- /**
- * We cannot directly mock the activity created since instrumentation creates it.
- * <p>
- * Instead, we use static instances of this object to modify behavior.
- */
- static class OverrideData {
- @SuppressWarnings("Since15")
- public Function<PackageManager, PackageManager> createPackageManager;
- public Function<Pair<TargetInfo, UserHandle>, Boolean> onSafelyStartInternalCallback;
- public ResolverListController resolverListController;
- public ResolverListController workResolverListController;
- public Boolean isVoiceInteraction;
- public AnnotatedUserHandles annotatedUserHandles;
- public Integer myUserId;
- public boolean hasCrossProfileIntents;
- public boolean isQuietModeEnabled;
- public WorkProfileAvailabilityManager mWorkProfileAvailability;
- public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
-
- public void reset() {
- onSafelyStartInternalCallback = null;
- isVoiceInteraction = null;
- createPackageManager = null;
- resolverListController = mock(ResolverListController.class);
- workResolverListController = mock(ResolverListController.class);
- annotatedUserHandles = AnnotatedUserHandles.newBuilder()
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(UserHandle.SYSTEM)
- .setPersonalProfileUserHandle(UserHandle.SYSTEM)
- .build();
- myUserId = null;
- hasCrossProfileIntents = true;
- isQuietModeEnabled = false;
-
- mWorkProfileAvailability = new WorkProfileAvailabilityManager(null, null, null) {
- @Override
- public boolean isQuietModeEnabled() {
- return isQuietModeEnabled;
- }
-
- @Override
- public boolean isWorkProfileUserUnlocked() {
- return true;
- }
-
- @Override
- public void requestQuietModeEnabled(boolean enabled) {
- isQuietModeEnabled = enabled;
- }
-
- @Override
- public void markWorkProfileEnabledBroadcastReceived() {}
-
- @Override
- public boolean isWaitingToEnableWorkProfile() {
- return false;
- }
- };
-
- mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class);
- when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt()))
- .thenAnswer(invocation -> hasCrossProfileIntents);
- }
- }
-
- private static class TargetDataLoaderWrapper extends TargetDataLoader {
- private final TargetDataLoader mTargetDataLoader;
- private final CountingIdlingResource mLabelIdlingResource;
-
- private TargetDataLoaderWrapper(
- TargetDataLoader targetDataLoader, CountingIdlingResource labelIdlingResource) {
- mTargetDataLoader = targetDataLoader;
- mLabelIdlingResource = labelIdlingResource;
- }
-
- @Override
- public void loadAppTargetIcon(
- @NonNull DisplayResolveInfo info,
- @NonNull UserHandle userHandle,
- @NonNull Consumer<Drawable> callback) {
- mTargetDataLoader.loadAppTargetIcon(info, userHandle, callback);
- }
-
- @Override
- public void loadDirectShareIcon(
- @NonNull SelectableTargetInfo info,
- @NonNull UserHandle userHandle,
- @NonNull Consumer<Drawable> callback) {
- mTargetDataLoader.loadDirectShareIcon(info, userHandle, callback);
- }
-
- @Override
- public void loadLabel(
- @NonNull DisplayResolveInfo info,
- @NonNull Consumer<LabelInfo> callback) {
- mLabelIdlingResource.increment();
- mTargetDataLoader.loadLabel(
- info,
- (result) -> {
- mLabelIdlingResource.decrement();
- callback.accept(result);
- });
- }
-
- @Override
- public void getOrLoadLabel(@NonNull DisplayResolveInfo info) {
- mTargetDataLoader.getOrLoadLabel(info);
- }
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/ShortcutSelectionLogicTest.kt b/java/tests/src/com/android/intentresolver/ShortcutSelectionLogicTest.kt
deleted file mode 100644
index 2346d98b..00000000
--- a/java/tests/src/com/android/intentresolver/ShortcutSelectionLogicTest.kt
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * 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.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.content.pm.ShortcutInfo
-import android.os.UserHandle
-import android.service.chooser.ChooserTarget
-import com.android.intentresolver.chooser.DisplayResolveInfo
-import com.android.intentresolver.chooser.TargetInfo
-import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertTrue
-import org.junit.Test
-
-private const val PACKAGE_A = "package.a"
-private const val PACKAGE_B = "package.b"
-private const val CLASS_NAME = "./MainActivity"
-
-@SmallTest
-class ShortcutSelectionLogicTest {
- private val PERSONAL_USER_HANDLE: UserHandle = InstrumentationRegistry
- .getInstrumentation().getTargetContext().getUser()
-
- private val packageTargets = HashMap<String, Array<ChooserTarget>>().apply {
- arrayOf(PACKAGE_A, PACKAGE_B).forEach { pkg ->
- // shortcuts in reverse priority order
- val targets = Array(3) { i ->
- createChooserTarget(
- "Shortcut $i",
- (i + 1).toFloat() / 10f,
- ComponentName(pkg, CLASS_NAME),
- pkg.shortcutId(i),
- )
- }
- this[pkg] = targets
- }
- }
-
- private val baseDisplayInfo = DisplayResolveInfo.newDisplayResolveInfo(
- Intent(),
- ResolverDataProvider.createResolveInfo(3, 0, PERSONAL_USER_HANDLE),
- "label",
- "extended info",
- Intent()
- )
-
- private val otherBaseDisplayInfo = DisplayResolveInfo.newDisplayResolveInfo(
- Intent(),
- ResolverDataProvider.createResolveInfo(4, 0, PERSONAL_USER_HANDLE),
- "label 2",
- "extended info 2",
- Intent()
- )
-
- private operator fun Map<String, Array<ChooserTarget>>.get(pkg: String, idx: Int) =
- this[pkg]?.get(idx) ?: error("missing package $pkg")
-
- @Test
- fun testAddShortcuts_no_limits() {
- val serviceResults = ArrayList<TargetInfo>()
- val sc1 = packageTargets[PACKAGE_A, 0]
- val sc2 = packageTargets[PACKAGE_A, 1]
- val testSubject = ShortcutSelectionLogic(
- /* maxShortcutTargetsPerApp = */ 1,
- /* applySharingAppLimits = */ false
- )
-
- val isUpdated = testSubject.addServiceResults(
- /* origTarget = */ baseDisplayInfo,
- /* origTargetScore = */ 0.1f,
- /* targets = */ listOf(sc1, sc2),
- /* isShortcutResult = */ true,
- /* directShareToShortcutInfos = */ emptyMap(),
- /* directShareToAppTargets = */ emptyMap(),
- /* userContext = */ mock(),
- /* targetIntent = */ mock(),
- /* refererFillInIntent = */ mock(),
- /* maxRankedTargets = */ 4,
- /* serviceTargets = */ serviceResults
- )
-
- assertTrue("Updates are expected", isUpdated)
- assertShortcutsInOrder(
- listOf(sc2, sc1),
- serviceResults,
- "Two shortcuts are expected as we do not apply per-app shortcut limit"
- )
- }
-
- @Test
- fun testAddShortcuts_same_package_with_per_package_limit() {
- val serviceResults = ArrayList<TargetInfo>()
- val sc1 = packageTargets[PACKAGE_A, 0]
- val sc2 = packageTargets[PACKAGE_A, 1]
- val testSubject = ShortcutSelectionLogic(
- /* maxShortcutTargetsPerApp = */ 1,
- /* applySharingAppLimits = */ true
- )
-
- val isUpdated = testSubject.addServiceResults(
- /* origTarget = */ baseDisplayInfo,
- /* origTargetScore = */ 0.1f,
- /* targets = */ listOf(sc1, sc2),
- /* isShortcutResult = */ true,
- /* directShareToShortcutInfos = */ emptyMap(),
- /* directShareToAppTargets = */ emptyMap(),
- /* userContext = */ mock(),
- /* targetIntent = */ mock(),
- /* refererFillInIntent = */ mock(),
- /* maxRankedTargets = */ 4,
- /* serviceTargets = */ serviceResults
- )
-
- assertTrue("Updates are expected", isUpdated)
- assertShortcutsInOrder(
- listOf(sc2),
- serviceResults,
- "One shortcut is expected as we apply per-app shortcut limit"
- )
- }
-
- @Test
- fun testAddShortcuts_same_package_no_per_app_limit_with_target_limit() {
- val serviceResults = ArrayList<TargetInfo>()
- val sc1 = packageTargets[PACKAGE_A, 0]
- val sc2 = packageTargets[PACKAGE_A, 1]
- val testSubject = ShortcutSelectionLogic(
- /* maxShortcutTargetsPerApp = */ 1,
- /* applySharingAppLimits = */ false
- )
-
- val isUpdated = testSubject.addServiceResults(
- /* origTarget = */ baseDisplayInfo,
- /* origTargetScore = */ 0.1f,
- /* targets = */ listOf(sc1, sc2),
- /* isShortcutResult = */ true,
- /* directShareToShortcutInfos = */ emptyMap(),
- /* directShareToAppTargets = */ emptyMap(),
- /* userContext = */ mock(),
- /* targetIntent = */ mock(),
- /* refererFillInIntent = */ mock(),
- /* maxRankedTargets = */ 1,
- /* serviceTargets = */ serviceResults
- )
-
- assertTrue("Updates are expected", isUpdated)
- assertShortcutsInOrder(
- listOf(sc2),
- serviceResults,
- "One shortcut is expected as we apply overall shortcut limit"
- )
- }
-
- @Test
- fun testAddShortcuts_different_packages_with_per_package_limit() {
- val serviceResults = ArrayList<TargetInfo>()
- val pkgAsc1 = packageTargets[PACKAGE_A, 0]
- val pkgAsc2 = packageTargets[PACKAGE_A, 1]
- val pkgBsc1 = packageTargets[PACKAGE_B, 0]
- val pkgBsc2 = packageTargets[PACKAGE_B, 1]
- val testSubject = ShortcutSelectionLogic(
- /* maxShortcutTargetsPerApp = */ 1,
- /* applySharingAppLimits = */ true
- )
-
- testSubject.addServiceResults(
- /* origTarget = */ baseDisplayInfo,
- /* origTargetScore = */ 0.1f,
- /* targets = */ listOf(pkgAsc1, pkgAsc2),
- /* isShortcutResult = */ true,
- /* directShareToShortcutInfos = */ emptyMap(),
- /* directShareToAppTargets = */ emptyMap(),
- /* userContext = */ mock(),
- /* targetIntent = */ mock(),
- /* refererFillInIntent = */ mock(),
- /* maxRankedTargets = */ 4,
- /* serviceTargets = */ serviceResults
- )
- testSubject.addServiceResults(
- /* origTarget = */ otherBaseDisplayInfo,
- /* origTargetScore = */ 0.2f,
- /* targets = */ listOf(pkgBsc1, pkgBsc2),
- /* isShortcutResult = */ true,
- /* directShareToShortcutInfos = */ emptyMap(),
- /* directShareToAppTargets = */ emptyMap(),
- /* userContext = */ mock(),
- /* targetIntent = */ mock(),
- /* refererFillInIntent = */ mock(),
- /* maxRankedTargets = */ 4,
- /* serviceTargets = */ serviceResults
- )
-
- assertShortcutsInOrder(
- listOf(pkgBsc2, pkgAsc2),
- serviceResults,
- "Two shortcuts are expected as we apply per-app shortcut limit"
- )
- }
-
- @Test
- fun testAddShortcuts_pinned_shortcut() {
- val serviceResults = ArrayList<TargetInfo>()
- val sc1 = packageTargets[PACKAGE_A, 0]
- val sc2 = packageTargets[PACKAGE_A, 1]
- val testSubject = ShortcutSelectionLogic(
- /* maxShortcutTargetsPerApp = */ 1,
- /* applySharingAppLimits = */ false
- )
-
- val isUpdated = testSubject.addServiceResults(
- /* origTarget = */ baseDisplayInfo,
- /* origTargetScore = */ 0.1f,
- /* targets = */ listOf(sc1, sc2),
- /* isShortcutResult = */ true,
- /* directShareToShortcutInfos = */ mapOf(
- sc1 to createShortcutInfo(
- PACKAGE_A.shortcutId(1),
- sc1.componentName, 1).apply {
- addFlags(ShortcutInfo.FLAG_PINNED)
- }
- ),
- /* directShareToAppTargets = */ emptyMap(),
- /* userContext = */ mock(),
- /* targetIntent = */ mock(),
- /* refererFillInIntent = */ mock(),
- /* maxRankedTargets = */ 4,
- /* serviceTargets = */ serviceResults
- )
-
- assertTrue("Updates are expected", isUpdated)
- assertShortcutsInOrder(
- listOf(sc1, sc2),
- serviceResults,
- "Two shortcuts are expected as we do not apply per-app shortcut limit"
- )
- }
-
- @Test
- fun test_available_caller_shortcuts_count_is_limited() {
- val serviceResults = ArrayList<TargetInfo>()
- val sc1 = packageTargets[PACKAGE_A, 0]
- val sc2 = packageTargets[PACKAGE_A, 1]
- val sc3 = packageTargets[PACKAGE_A, 2]
- val testSubject = ShortcutSelectionLogic(
- /* maxShortcutTargetsPerApp = */ 1,
- /* applySharingAppLimits = */ true
- )
- val context = mock<Context> {
- whenever(packageManager).thenReturn(mock())
- }
-
- testSubject.addServiceResults(
- /* origTarget = */ baseDisplayInfo,
- /* origTargetScore = */ 0f,
- /* targets = */ listOf(sc1, sc2, sc3),
- /* isShortcutResult = */ false,
- /* directShareToShortcutInfos = */ emptyMap(),
- /* directShareToAppTargets = */ emptyMap(),
- /* userContext = */ context,
- /* targetIntent = */ mock(),
- /* refererFillInIntent = */ mock(),
- /* maxRankedTargets = */ 4,
- /* serviceTargets = */ serviceResults
- )
-
- assertShortcutsInOrder(
- listOf(sc3, sc2),
- serviceResults,
- "At most two caller-provided shortcuts are allowed"
- )
- }
-
- // TODO: consider renaming. Not all `ChooserTarget`s are "shortcuts" and many of our test cases
- // add results with `isShortcutResult = false` and `directShareToShortcutInfos = emptyMap()`.
- private fun assertShortcutsInOrder(
- expected: List<ChooserTarget>, actual: List<TargetInfo>, msg: String? = ""
- ) {
- assertEquals(msg, expected.size, actual.size)
- for (i in expected.indices) {
- assertEquals(
- "Unexpected item at position $i",
- expected[i].componentName,
- actual[i].chooserTargetComponentName
- )
- assertEquals(
- "Unexpected item at position $i",
- expected[i].title,
- actual[i].displayLabel
- )
- }
- }
-
- private fun String.shortcutId(id: Int) = "$this.$id"
-}
diff --git a/java/tests/src/com/android/intentresolver/TargetPresentationGetterTest.kt b/java/tests/src/com/android/intentresolver/TargetPresentationGetterTest.kt
deleted file mode 100644
index e62672a3..00000000
--- a/java/tests/src/com/android/intentresolver/TargetPresentationGetterTest.kt
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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 com.android.intentresolver.ResolverDataProvider
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-
-/**
- * Unit tests for the various implementations of {@link TargetPresentationGetter}.
- * TODO: consider expanding to cover icon logic (not just labels/sublabels).
- * TODO: these are conceptually "acceptance tests" that provide comprehensive coverage of the
- * apparent variations in the legacy implementation. The tests probably don't have to be so
- * exhaustive if we're able to impose a simpler design on the implementation.
- */
-class TargetPresentationGetterTest {
- fun makeResolveInfoPresentationGetter(
- withSubstitutePermission: Boolean,
- appLabel: String,
- activityLabel: String,
- resolveInfoLabel: String): TargetPresentationGetter {
- val testPackageInfo = ResolverDataProvider.createPackageManagerMockedInfo(
- withSubstitutePermission, appLabel, activityLabel, resolveInfoLabel)
- val factory = TargetPresentationGetter.Factory(testPackageInfo.ctx, 100)
- return factory.makePresentationGetter(testPackageInfo.resolveInfo)
- }
-
- fun makeActivityInfoPresentationGetter(
- withSubstitutePermission: Boolean,
- appLabel: String?,
- activityLabel: String?): TargetPresentationGetter {
- val testPackageInfo = ResolverDataProvider.createPackageManagerMockedInfo(
- withSubstitutePermission, appLabel, activityLabel, "")
- val factory = TargetPresentationGetter.Factory(testPackageInfo.ctx, 100)
- return factory.makePresentationGetter(testPackageInfo.activityInfo)
- }
-
- @Test
- fun testActivityInfoLabels_noSubstitutePermission_distinctRequestedLabelAndSublabel() {
- val presentationGetter = makeActivityInfoPresentationGetter(
- false, "app_label", "activity_label")
- assertThat(presentationGetter.getLabel()).isEqualTo("app_label")
- assertThat(presentationGetter.getSubLabel()).isEqualTo("activity_label")
- }
-
- @Test
- fun testActivityInfoLabels_noSubstitutePermission_sameRequestedLabelAndSublabel() {
- val presentationGetter = makeActivityInfoPresentationGetter(
- false, "app_label", "app_label")
- assertThat(presentationGetter.getLabel()).isEqualTo("app_label")
- // Without the substitute permission, there's no logic to dedupe the labels.
- // TODO: this matches our observations in the legacy code, but is it the right behavior? It
- // seems like {@link ResolverListAdapter.ViewHolder#bindLabel()} has some logic to dedupe in
- // the UI at least, but maybe that logic should be pulled back to the "presentation"?
- assertThat(presentationGetter.getSubLabel()).isEqualTo("app_label")
- }
-
- @Test
- fun testActivityInfoLabels_noSubstitutePermission_nullRequestedLabel() {
- val presentationGetter = makeActivityInfoPresentationGetter(false, null, "activity_label")
- assertThat(presentationGetter.getLabel()).isNull()
- assertThat(presentationGetter.getSubLabel()).isEqualTo("activity_label")
- }
-
- @Test
- fun testActivityInfoLabels_noSubstitutePermission_emptyRequestedLabel() {
- val presentationGetter = makeActivityInfoPresentationGetter(false, "", "activity_label")
- assertThat(presentationGetter.getLabel()).isEqualTo("")
- assertThat(presentationGetter.getSubLabel()).isEqualTo("activity_label")
- }
-
- @Test
- fun testActivityInfoLabels_noSubstitutePermission_emptyRequestedSublabel() {
- val presentationGetter = makeActivityInfoPresentationGetter(false, "app_label", "")
- assertThat(presentationGetter.getLabel()).isEqualTo("app_label")
- // Without the substitute permission, empty sublabels are passed through as-is.
- assertThat(presentationGetter.getSubLabel()).isEqualTo("")
- }
-
- @Test
- fun testActivityInfoLabels_withSubstitutePermission_distinctRequestedLabelAndSublabel() {
- val presentationGetter = makeActivityInfoPresentationGetter(
- true, "app_label", "activity_label")
- assertThat(presentationGetter.getLabel()).isEqualTo("activity_label")
- // With the substitute permission, the same ("activity") label is requested as both the label
- // and sublabel, even though the other value ("app_label") was distinct. Thus this behaves the
- // same as a dupe.
- assertThat(presentationGetter.getSubLabel()).isEqualTo(null)
- }
-
- @Test
- fun testActivityInfoLabels_withSubstitutePermission_sameRequestedLabelAndSublabel() {
- val presentationGetter = makeActivityInfoPresentationGetter(
- true, "app_label", "app_label")
- assertThat(presentationGetter.getLabel()).isEqualTo("app_label")
- // With the substitute permission, duped sublabels get converted to nulls.
- assertThat(presentationGetter.getSubLabel()).isNull()
- }
-
- @Test
- fun testActivityInfoLabels_withSubstitutePermission_nullRequestedLabel() {
- val presentationGetter = makeActivityInfoPresentationGetter(true, "app_label", null)
- assertThat(presentationGetter.getLabel()).isEqualTo("app_label")
- // With the substitute permission, null inputs are a special case that produces null outputs
- // (i.e., they're not simply passed-through from the inputs).
- assertThat(presentationGetter.getSubLabel()).isNull()
- }
-
- @Test
- fun testActivityInfoLabels_withSubstitutePermission_emptyRequestedLabel() {
- val presentationGetter = makeActivityInfoPresentationGetter(true, "app_label", "")
- // Empty "labels" are taken as-is and (unlike nulls) don't prompt a fallback to the sublabel.
- // Thus (as in the previous case with substitute permission & "distinct" labels), this is
- // treated as a dupe.
- assertThat(presentationGetter.getLabel()).isEqualTo("")
- assertThat(presentationGetter.getSubLabel()).isNull()
- }
-
- @Test
- fun testActivityInfoLabels_withSubstitutePermission_emptyRequestedSublabel() {
- val presentationGetter = makeActivityInfoPresentationGetter(true, "", "activity_label")
- assertThat(presentationGetter.getLabel()).isEqualTo("activity_label")
- // With the substitute permission, empty sublabels get converted to nulls.
- assertThat(presentationGetter.getSubLabel()).isNull()
- }
-
- @Test
- fun testResolveInfoLabels_noSubstitutePermission_distinctRequestedLabelAndSublabel() {
- val presentationGetter = makeResolveInfoPresentationGetter(
- false, "app_label", "activity_label", "resolve_info_label")
- assertThat(presentationGetter.getLabel()).isEqualTo("app_label")
- assertThat(presentationGetter.getSubLabel()).isEqualTo("resolve_info_label")
- }
-
- @Test
- fun testResolveInfoLabels_noSubstitutePermission_sameRequestedLabelAndSublabel() {
- val presentationGetter = makeResolveInfoPresentationGetter(
- false, "app_label", "activity_label", "app_label")
- assertThat(presentationGetter.getLabel()).isEqualTo("app_label")
- // Without the substitute permission, there's no logic to dedupe the labels.
- // TODO: this matches our observations in the legacy code, but is it the right behavior? It
- // seems like {@link ResolverListAdapter.ViewHolder#bindLabel()} has some logic to dedupe in
- // the UI at least, but maybe that logic should be pulled back to the "presentation"?
- assertThat(presentationGetter.getSubLabel()).isEqualTo("app_label")
- }
-
- @Test
- fun testResolveInfoLabels_noSubstitutePermission_emptyRequestedSublabel() {
- val presentationGetter = makeResolveInfoPresentationGetter(
- false, "app_label", "activity_label", "")
- assertThat(presentationGetter.getLabel()).isEqualTo("app_label")
- // Without the substitute permission, empty sublabels are passed through as-is.
- assertThat(presentationGetter.getSubLabel()).isEqualTo("")
- }
-
- @Test
- fun testResolveInfoLabels_withSubstitutePermission_distinctRequestedLabelAndSublabel() {
- val presentationGetter = makeResolveInfoPresentationGetter(
- true, "app_label", "activity_label", "resolve_info_label")
- assertThat(presentationGetter.getLabel()).isEqualTo("activity_label")
- assertThat(presentationGetter.getSubLabel()).isEqualTo("resolve_info_label")
- }
-
- @Test
- fun testResolveInfoLabels_withSubstitutePermission_sameRequestedLabelAndSublabel() {
- val presentationGetter = makeResolveInfoPresentationGetter(
- true, "app_label", "activity_label", "activity_label")
- assertThat(presentationGetter.getLabel()).isEqualTo("activity_label")
- // With the substitute permission, duped sublabels get converted to nulls.
- assertThat(presentationGetter.getSubLabel()).isNull()
- }
-
- @Test
- fun testResolveInfoLabels_withSubstitutePermission_emptyRequestedSublabel() {
- val presentationGetter = makeResolveInfoPresentationGetter(
- true, "app_label", "activity_label", "")
- assertThat(presentationGetter.getLabel()).isEqualTo("activity_label")
- // With the substitute permission, empty sublabels get converted to nulls.
- assertThat(presentationGetter.getSubLabel()).isNull()
- }
-
- @Test
- fun testResolveInfoLabels_withSubstitutePermission_emptyRequestedLabelAndSublabel() {
- val presentationGetter = makeResolveInfoPresentationGetter(
- true, "app_label", "", "")
- assertThat(presentationGetter.getLabel()).isEqualTo("")
- // With the substitute permission, empty sublabels get converted to nulls.
- assertThat(presentationGetter.getSubLabel()).isNull()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/TestContentPreviewViewModel.kt b/java/tests/src/com/android/intentresolver/TestContentPreviewViewModel.kt
deleted file mode 100644
index d239f612..00000000
--- a/java/tests/src/com/android/intentresolver/TestContentPreviewViewModel.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.viewmodel.CreationExtras
-import com.android.intentresolver.contentpreview.BasePreviewViewModel
-import com.android.intentresolver.contentpreview.ImageLoader
-import com.android.intentresolver.contentpreview.PreviewDataProvider
-
-/** A test content preview model that supports image loader override. */
-class TestContentPreviewViewModel(
- private val viewModel: BasePreviewViewModel,
- private val imageLoader: ImageLoader? = null,
-) : BasePreviewViewModel() {
- override fun createOrReuseProvider(
- chooserRequest: ChooserRequestParameters
- ): PreviewDataProvider = viewModel.createOrReuseProvider(chooserRequest)
-
- override fun createOrReuseImageLoader(): ImageLoader =
- imageLoader ?: viewModel.createOrReuseImageLoader()
-
- companion object {
- fun wrap(
- factory: ViewModelProvider.Factory,
- imageLoader: ImageLoader?,
- ): ViewModelProvider.Factory =
- object : ViewModelProvider.Factory {
- @Suppress("UNCHECKED_CAST")
- override fun <T : ViewModel> create(
- modelClass: Class<T>,
- extras: CreationExtras
- ): T {
- return TestContentPreviewViewModel(
- factory.create(modelClass, extras) as BasePreviewViewModel,
- imageLoader,
- ) as T
- }
- }
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/TestContentProvider.kt b/java/tests/src/com/android/intentresolver/TestContentProvider.kt
deleted file mode 100644
index 426f9af2..00000000
--- a/java/tests/src/com/android/intentresolver/TestContentProvider.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver
-
-import android.content.ContentProvider
-import android.content.ContentValues
-import android.database.Cursor
-import android.net.Uri
-
-class TestContentProvider : ContentProvider() {
- override fun query(
- uri: Uri,
- projection: Array<out String>?,
- selection: String?,
- selectionArgs: Array<out String>?,
- sortOrder: String?
- ): Cursor? = null
-
- override fun getType(uri: Uri): String? =
- runCatching { uri.getQueryParameter(PARAM_MIME_TYPE) }.getOrNull()
-
- override fun getStreamTypes(uri: Uri, mimeTypeFilter: String): Array<String>? {
- val delay =
- runCatching { uri.getQueryParameter(PARAM_STREAM_TYPE_TIMEOUT)?.toLong() ?: 0L }
- .getOrDefault(0L)
- if (delay > 0) {
- try {
- Thread.sleep(delay)
- } catch (e: InterruptedException) {
- Thread.currentThread().interrupt()
- }
- }
- return runCatching { uri.getQueryParameter(PARAM_STREAM_TYPE)?.let { arrayOf(it) } }
- .getOrNull()
- }
-
- override fun insert(uri: Uri, values: ContentValues?): Uri? = null
-
- override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int = 0
-
- override fun update(
- uri: Uri,
- values: ContentValues?,
- selection: String?,
- selectionArgs: Array<out String>?
- ): Int = 0
-
- override fun onCreate(): Boolean = true
-
- companion object {
- const val PARAM_MIME_TYPE = "mimeType"
- const val PARAM_STREAM_TYPE = "streamType"
- const val PARAM_STREAM_TYPE_TIMEOUT = "streamTypeTo"
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/TestHelpers.kt b/java/tests/src/com/android/intentresolver/TestHelpers.kt
deleted file mode 100644
index 5b583fef..00000000
--- a/java/tests/src/com/android/intentresolver/TestHelpers.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.app.prediction.AppTarget
-import android.app.prediction.AppTargetId
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.content.pm.ShortcutInfo
-import android.content.pm.ShortcutManager.ShareShortcutInfo
-import android.os.Bundle
-import android.service.chooser.ChooserTarget
-import org.mockito.Mockito.`when` as whenever
-
-internal fun createShareShortcutInfo(
- id: String,
- componentName: ComponentName,
- rank: Int
-): ShareShortcutInfo =
- ShareShortcutInfo(
- createShortcutInfo(id, componentName, rank),
- componentName
- )
-
-internal fun createShortcutInfo(
- id: String,
- componentName: ComponentName,
- rank: Int
-): ShortcutInfo {
- val context = mock<Context>()
- whenever(context.packageName).thenReturn(componentName.packageName)
- return ShortcutInfo.Builder(context, id)
- .setShortLabel("Short Label $id")
- .setLongLabel("Long Label $id")
- .setActivity(componentName)
- .setRank(rank)
- .build()
-}
-
-internal fun createAppTarget(shortcutInfo: ShortcutInfo) =
- AppTarget(
- AppTargetId(shortcutInfo.id),
- shortcutInfo,
- shortcutInfo.activity?.className ?: error("missing activity info")
- )
-
-fun createChooserTarget(
- title: String, score: Float, componentName: ComponentName, shortcutId: String
-): ChooserTarget =
- ChooserTarget(
- title,
- null,
- score,
- componentName,
- Bundle().apply { putString(Intent.EXTRA_SHORTCUT_ID, shortcutId) }
- )
diff --git a/java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt b/java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt
deleted file mode 100644
index 9c4d6187..00000000
--- a/java/tests/src/com/android/intentresolver/TestPreviewImageLoader.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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 com.android.intentresolver.contentpreview.ImageLoader
-import java.util.function.Consumer
-import kotlinx.coroutines.CoroutineScope
-
-internal class TestPreviewImageLoader(private val bitmaps: Map<Uri, Bitmap>) : ImageLoader {
- override fun loadImage(callerScope: CoroutineScope, uri: Uri, callback: Consumer<Bitmap?>) {
- callback.accept(bitmaps[uri])
- }
-
- override suspend fun invoke(uri: Uri, caching: Boolean): Bitmap? = bitmaps[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
deleted file mode 100644
index f597d7f2..00000000
--- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java
+++ /dev/null
@@ -1,3127 +0,0 @@
-/*
- * Copyright (C) 2016 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 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;
-import static androidx.test.espresso.matcher.ViewMatchers.hasSibling;
-import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-
-import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_CHOOSER_TARGET;
-import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_DEFAULT;
-import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
-import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
-import static com.android.intentresolver.ChooserListAdapter.CALLER_TARGET_SCORE_BOOST;
-import static com.android.intentresolver.ChooserListAdapter.SHORTCUT_TARGET_SCORE_BOOST;
-import static com.android.intentresolver.MatcherUtils.first;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static junit.framework.Assert.assertNull;
-
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-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;
-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;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.Icon;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.provider.DeviceConfig;
-import android.service.chooser.ChooserAction;
-import android.service.chooser.ChooserTarget;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.style.BackgroundColorSpan;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.StyleSpan;
-import android.text.style.UnderlineSpan;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-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.espresso.matcher.ViewMatchers;
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-
-import com.android.intentresolver.chooser.DisplayResolveInfo;
-import com.android.intentresolver.contentpreview.ImageLoader;
-import com.android.intentresolver.logging.EventLog;
-import com.android.intentresolver.logging.FakeEventLog;
-import com.android.intentresolver.shortcuts.ShortcutLoader;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import dagger.hilt.android.testing.HiltAndroidRule;
-import dagger.hilt.android.testing.HiltAndroidTest;
-
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.Matchers;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-/**
- * Instrumentation tests for ChooserActivity.
- * <p>
- * Legacy test suite migrated from framework CoreTests.
- * <p>
- */
-@RunWith(Parameterized.class)
-@HiltAndroidTest
-public class UnbundledChooserActivityTest {
-
- private static FakeEventLog getEventLog(ChooserWrapperActivity activity) {
- return (FakeEventLog) activity.mEventLog;
- }
-
- private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
- .getInstrumentation().getTargetContext().getUser();
- private static final UserHandle WORK_PROFILE_USER_HANDLE = UserHandle.of(10);
- private static final UserHandle CLONE_PROFILE_USER_HANDLE = UserHandle.of(11);
-
- private static final Function<PackageManager, PackageManager> DEFAULT_PM = pm -> pm;
- private static final Function<PackageManager, PackageManager> NO_APP_PREDICTION_SERVICE_PM =
- pm -> {
- PackageManager mock = Mockito.spy(pm);
- when(mock.getAppPredictionServicePackageName()).thenReturn(null);
- return mock;
- };
-
- @Parameterized.Parameters
- public static Collection packageManagers() {
- return Arrays.asList(new Object[][] {
- // Default PackageManager
- { DEFAULT_PM },
- // No App Prediction Service
- { NO_APP_PREDICTION_SERVICE_PM}
- });
- }
-
- private static final String TEST_MIME_TYPE = "application/TestType";
-
- private static final int CONTENT_PREVIEW_IMAGE = 1;
- private static final int CONTENT_PREVIEW_FILE = 2;
- private static final int CONTENT_PREVIEW_TEXT = 3;
-
- @Rule(order = 0)
- public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Rule(order = 1)
- public HiltAndroidRule mHiltAndroidRule = new HiltAndroidRule(this);
-
- @Rule(order = 2)
- public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
- new ActivityTestRule<>(ChooserWrapperActivity.class, false, false);
-
- @Before
- public void setUp() {
- // TODO: use the other form of `adoptShellPermissionIdentity()` where we explicitly list the
- // permissions we require (which we'll read from the manifest at runtime).
- InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .adoptShellPermissionIdentity();
-
- cleanOverrideData();
- mHiltAndroidRule.inject();
- }
-
- private final Function<PackageManager, PackageManager> mPackageManagerOverride;
-
- public UnbundledChooserActivityTest(
- Function<PackageManager, PackageManager> packageManagerOverride) {
- mPackageManagerOverride = packageManagerOverride;
- }
-
- private void setDeviceConfigProperty(
- @NonNull String propertyName,
- @NonNull String value) {
- // TODO: consider running with {@link #runWithShellPermissionIdentity()} to more narrowly
- // request WRITE_DEVICE_CONFIG permissions if we get rid of the broad grant we currently
- // configure in {@link #setup()}.
- // TODO: is it really appropriate that this is always set with makeDefault=true?
- boolean valueWasSet = DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- propertyName,
- value,
- true /* makeDefault */);
- if (!valueWasSet) {
- throw new IllegalStateException(
- "Could not set " + propertyName + " to " + value);
- }
- }
-
- public void cleanOverrideData() {
- ChooserActivityOverrideData.getInstance().reset();
- ChooserActivityOverrideData.getInstance().createPackageManager = mPackageManagerOverride;
-
- setDeviceConfigProperty(
- SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
- Boolean.toString(true));
- }
-
- @Test
- public void customTitle() throws InterruptedException {
- Intent viewIntent = createViewTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- final IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(
- Intent.createChooser(viewIntent, "chooser test"));
-
- waitForIdle();
- assertThat(activity.getAdapter().getCount(), is(2));
- assertThat(activity.getAdapter().getServiceTargetCount(), is(0));
- onView(withId(android.R.id.title)).check(matches(withText("chooser test")));
- }
-
- @Test
- public void customTitleIgnoredForSendIntents() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "chooser test"));
- waitForIdle();
- onView(withId(android.R.id.title))
- .check(matches(withText(R.string.whichSendApplication)));
- }
-
- @Test
- public void emptyTitle() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(android.R.id.title))
- .check(matches(withText(R.string.whichSendApplication)));
- }
-
- @Test
- public void test_shareRichTextWithRichTitle_richTextAndRichTitleDisplayed() {
- CharSequence title = new SpannableStringBuilder()
- .append("Rich", new UnderlineSpan(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
- .append(
- "Title",
- new ForegroundColorSpan(Color.RED),
- Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
- CharSequence sharedText = new SpannableStringBuilder()
- .append(
- "Rich",
- new BackgroundColorSpan(Color.YELLOW),
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
- .append(
- "Text",
- new StyleSpan(Typeface.ITALIC),
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- Intent sendIntent = createSendTextIntent();
- sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText);
- sendIntent.putExtra(Intent.EXTRA_TITLE, title);
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check((view, e) -> {
- assertThat(view).isInstanceOf(TextView.class);
- CharSequence text = ((TextView) view).getText();
- assertThat(text).isInstanceOf(Spanned.class);
- Spanned spanned = (Spanned) text;
- assertThat(spanned.getSpans(0, spanned.length(), Object.class))
- .hasLength(2);
- assertThat(spanned.getSpans(0, 4, UnderlineSpan.class)).hasLength(1);
- assertThat(spanned.getSpans(4, spanned.length(), ForegroundColorSpan.class))
- .hasLength(1);
- });
-
- onView(withId(com.android.internal.R.id.content_preview_text))
- .check((view, e) -> {
- assertThat(view).isInstanceOf(TextView.class);
- CharSequence text = ((TextView) view).getText();
- assertThat(text).isInstanceOf(Spanned.class);
- Spanned spanned = (Spanned) text;
- assertThat(spanned.getSpans(0, spanned.length(), Object.class))
- .hasLength(2);
- assertThat(spanned.getSpans(0, 4, BackgroundColorSpan.class)).hasLength(1);
- assertThat(spanned.getSpans(4, spanned.length(), StyleSpan.class)).hasLength(1);
- });
- }
-
- @Test
- public void emptyPreviewTitleAndThumbnail() throws InterruptedException {
- Intent sendIntent = createSendTextIntentWithPreview(null, null);
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check(matches(not(isDisplayed())));
- onView(withId(com.android.internal.R.id.content_preview_thumbnail))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void visiblePreviewTitleWithoutThumbnail() throws InterruptedException {
- String previewTitle = "My Content Preview Title";
- Intent sendIntent = createSendTextIntentWithPreview(previewTitle, null);
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check(matches(withText(previewTitle)));
- onView(withId(com.android.internal.R.id.content_preview_thumbnail))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void visiblePreviewTitleWithInvalidThumbnail() throws InterruptedException {
- String previewTitle = "My Content Preview Title";
- Intent sendIntent = createSendTextIntentWithPreview(previewTitle,
- Uri.parse("tel:(+49)12345789"));
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.content_preview_thumbnail))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void visiblePreviewTitleAndThumbnail() throws InterruptedException {
- String previewTitle = "My Content Preview Title";
- Uri uri = Uri.parse(
- "android.resource://com.android.frameworks.coretests/"
- + com.android.intentresolver.tests.R.drawable.test320x240);
- Intent sendIntent = createSendTextIntentWithPreview(previewTitle, uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.content_preview_thumbnail))
- .check(matches(isDisplayed()));
- }
-
- @Test @Ignore
- public void twoOptionsAndUserSelectsOne() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- assertThat(activity.getAdapter().getCount(), is(2));
- onView(withId(com.android.internal.R.id.profile_button)).check(doesNotExist());
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
- onView(withText(toChoose.activityInfo.name))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test @Ignore
- public void fourOptionsStackedIntoOneTarget() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
-
- // create just enough targets to ensure the a-z list should be shown
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1);
-
- // next create 4 targets in a single app that should be stacked into a single target
- String packageName = "xxx.yyy";
- String appName = "aaa";
- ComponentName cn = new ComponentName(packageName, appName);
- Intent intent = new Intent("fakeIntent");
- List<ResolvedComponentInfo> infosToStack = new ArrayList<>();
- for (int i = 0; i < 4; i++) {
- ResolveInfo resolveInfo = ResolverDataProvider.createResolveInfo(i,
- UserHandle.USER_CURRENT, PERSONAL_USER_HANDLE);
- resolveInfo.activityInfo.applicationInfo.name = appName;
- resolveInfo.activityInfo.applicationInfo.packageName = packageName;
- resolveInfo.activityInfo.packageName = packageName;
- resolveInfo.activityInfo.name = "ccc" + i;
- infosToStack.add(new ResolvedComponentInfo(cn, intent, resolveInfo));
- }
- resolvedComponentInfos.addAll(infosToStack);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- // expect 1 unique targets + 1 group + 4 ranked app targets
- assertThat(activity.getAdapter().getCount(), is(6));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- onView(allOf(withText(appName), hasSibling(withText("")))).perform(click());
- waitForIdle();
-
- // clicking will launch a dialog to choose the activity within the app
- onView(withText(appName)).check(matches(isDisplayed()));
- int i = 0;
- for (ResolvedComponentInfo rci: infosToStack) {
- onView(withText("ccc" + i)).check(matches(isDisplayed()));
- ++i;
- }
- }
-
- @Test @Ignore
- public void updateChooserCountsAndModelAfterUserSelection() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- UsageStatsManager usm = activity.getUsageStatsManager();
- verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1))
- .topK(any(List.class), anyInt());
- assertThat(activity.getIsSelected(), is(false));
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- return true;
- };
- ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
- DisplayResolveInfo testDri =
- activity.createTestDisplayResolveInfo(
- sendIntent, toChoose, "testLabel", "testInfo", sendIntent);
- onView(withText(toChoose.activityInfo.name))
- .perform(click());
- waitForIdle();
- verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1))
- .updateChooserCounts(Mockito.anyString(), any(UserHandle.class),
- Mockito.anyString());
- verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1))
- .updateModel(testDri);
- assertThat(activity.getIsSelected(), is(true));
- }
-
- @Ignore // b/148158199
- @Test
- public void noResultsFromPackageManager() {
- setupResolverControllers(null);
- Intent sendIntent = createSendTextIntent();
- final ChooserActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- final IChooserWrapper wrapper = (IChooserWrapper) activity;
-
- waitForIdle();
- assertThat(activity.isFinishing(), is(false));
-
- onView(withId(android.R.id.empty)).check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.profile_pager)).check(matches(not(isDisplayed())));
- InstrumentationRegistry.getInstrumentation().runOnMainSync(
- () -> wrapper.getAdapter().handlePackagesChanged()
- );
- // backward compatibility. looks like we finish when data is empty after package change
- assertThat(activity.isFinishing(), is(true));
- }
-
- @Test
- public void autoLaunchSingleResult() throws InterruptedException {
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1);
- setupResolverControllers(resolvedComponentInfos);
-
- Intent sendIntent = createSendTextIntent();
- final ChooserActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- assertThat(chosen[0], is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
- assertThat(activity.isFinishing(), is(true));
- }
-
- @Test @Ignore
- public void hasOtherProfileOneOption() {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
- Intent sendIntent = createSendTextIntent();
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- // The other entry is filtered to the other profile slot
- assertThat(activity.getAdapter().getCount(), is(1));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10);
- waitForIdle();
-
- onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test @Ignore
- public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- setupResolverControllers(resolvedComponentInfos);
- when(ChooserActivityOverrideData.getInstance().resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- // The other entry is filtered to the other profile slot
- assertThat(activity.getAdapter().getCount(), is(2));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(3);
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test @Ignore
- public void hasLastChosenActivityAndOtherProfile() throws Exception {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- // The other entry is filtered to the last used slot
- assertThat(activity.getAdapter().getCount(), is(2));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(3);
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test
- @Ignore("b/285309527")
- public void testFilePlusTextSharing_ExcludeText() {
- Uri uri = createTestContentProviderUri(null, "image/png");
- Intent sendIntent = createSendImageIntent(uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
- sendIntent.putExtra(Intent.EXTRA_TEXT, "https://google.com/search?q=google");
-
- List<ResolvedComponentInfo> resolvedComponentInfos = Arrays.asList(
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.imageviewer", "ImageTarget"),
- sendIntent, PERSONAL_USER_HANDLE),
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.textviewer", "UriTarget"),
- new Intent("VIEW_TEXT"), PERSONAL_USER_HANDLE)
- );
-
- 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.content_preview_text)).check(matches(withText("File only")));
-
- AtomicReference<Intent> launchedIntentRef = new AtomicReference<>();
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = 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
- @Ignore("b/285309527")
- public void testFilePlusTextSharing_RemoveAndAddBackText() {
- Uri uri = createTestContentProviderUri("application/pdf", "image/png");
- Intent sendIntent = createSendImageIntent(uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
- 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, PERSONAL_USER_HANDLE),
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.textviewer", "UriTarget"),
- new Intent("VIEW_TEXT"), PERSONAL_USER_HANDLE)
- );
-
- 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.content_preview_text)).check(matches(withText("File only")));
-
- onView(withId(R.id.include_text_action))
- .perform(click());
- waitForIdle();
-
- onView(withId(R.id.content_preview_text)).check(matches(withText(text)));
-
- AtomicReference<Intent> launchedIntentRef = new AtomicReference<>();
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = 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
- @Ignore("b/285309527")
- public void testFilePlusTextSharing_TextExclusionDoesNotAffectAlternativeIntent() {
- Uri uri = createTestContentProviderUri("image/png", null);
- Intent sendIntent = createSendImageIntent(uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
- 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, PERSONAL_USER_HANDLE),
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.textviewer", "UriTarget"),
- alternativeIntent, PERSONAL_USER_HANDLE)
- );
-
- 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().onSafelyStartInternalCallback = 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
- @Ignore("b/285309527")
- public void testImagePlusTextSharing_failedThumbnailAndExcludedText_textChanges() {
- Uri uri = createTestContentProviderUri("image/png", null);
- Intent sendIntent = createSendImageIntent(uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- new TestPreviewImageLoader(Collections.emptyMap());
- sendIntent.putExtra(Intent.EXTRA_TEXT, "https://google.com/search?q=google");
-
- List<ResolvedComponentInfo> resolvedComponentInfos = Arrays.asList(
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.imageviewer", "ImageTarget"),
- sendIntent, PERSONAL_USER_HANDLE),
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.textviewer", "UriTarget"),
- new Intent("VIEW_TEXT"), PERSONAL_USER_HANDLE)
- );
-
- 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.image_view))
- .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)));
- onView(withId(R.id.content_preview_text))
- .check(matches(allOf(isDisplayed(), withText("Image only"))));
- }
-
- @Test
- public void copyTextToClipboard() {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final ChooserActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(R.id.copy)).check(matches(isDisplayed()));
- onView(withId(R.id.copy)).perform(click());
- ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
- Context.CLIPBOARD_SERVICE);
- ClipData clipData = clipboard.getPrimaryClip();
- assertThat(clipData).isNotNull();
- assertThat(clipData.getItemAt(0).getText()).isEqualTo("testing intent sending");
-
- ClipDescription clipDescription = clipData.getDescription();
- assertThat("text/plain", is(clipDescription.getMimeType(0)));
-
- assertEquals(mActivityRule.getActivityResult().getResultCode(), RESULT_OK);
- }
-
- @Test
- public void copyTextToClipboardLogging() {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(R.id.copy)).check(matches(isDisplayed()));
- onView(withId(R.id.copy)).perform(click());
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getActionSelected())
- .isEqualTo(new FakeEventLog.ActionSelected(
- /* targetType = */ EventLog.SELECTION_TYPE_COPY));
- }
-
- @Test
- @Ignore
- public void testNearbyShareLogging() throws Exception {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.chooser_nearby_button))
- .check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.chooser_nearby_button)).perform(click());
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
-
-
- @Test @Ignore
- public void testEditImageLogs() {
- Uri uri = createTestContentProviderUri("image/png", null);
- Intent sendIntent = createSendImageIntent(uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.chooser_edit_button)).check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.chooser_edit_button)).perform(click());
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
-
- @Test
- public void oneVisibleImagePreview() {
- Uri uri = createTestContentProviderUri("image/png", null);
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createWideBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.scrollable_image_preview))
- .check((view, exception) -> {
- if (exception != null) {
- throw exception;
- }
- RecyclerView recyclerView = (RecyclerView) view;
- assertThat(recyclerView.getAdapter().getItemCount(), is(1));
- assertThat(recyclerView.getChildCount(), is(1));
- View imageView = recyclerView.getChildAt(0);
- Rect rect = new Rect();
- boolean isPartiallyVisible = imageView.getGlobalVisibleRect(rect);
- assertThat(
- "image preview view is not fully visible",
- isPartiallyVisible
- && rect.width() == imageView.getWidth()
- && rect.height() == imageView.getHeight());
- });
- }
-
- @Test
- public void allThumbnailsFailedToLoad_hidePreview() {
- Uri uri = createTestContentProviderUri("image/jpg", null);
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- new TestPreviewImageLoader(Collections.emptyMap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.scrollable_image_preview))
- .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)));
- }
-
- @Test(timeout = 4_000)
- public void testSlowUriMetadata_fallbackToFilePreview() {
- Uri uri = createTestContentProviderUri(
- "application/pdf", "image/png", /*streamTypeTimeout=*/8_000);
- ArrayList<Uri> uris = new ArrayList<>(1);
- uris.add(uri);
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- // The preview type resolution is expected to timeout and default to file preview, otherwise
- // the test should timeout.
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("image.png")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
- @Test(timeout = 4_000)
- public void testSendManyFilesWithSmallMetadataDelayAndOneImage_fallbackToFilePreviewUi() {
- Uri fileUri = createTestContentProviderUri(
- "application/pdf", "application/pdf", /*streamTypeTimeout=*/300);
- Uri imageUri = createTestContentProviderUri("application/pdf", "image/png");
- ArrayList<Uri> uris = new ArrayList<>(50);
- for (int i = 0; i < 49; i++) {
- uris.add(fileUri);
- }
- uris.add(imageUri);
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(imageUri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos);
- // The preview type resolution is expected to timeout and default to file preview, otherwise
- // the test should timeout.
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
-
- waitForIdle();
-
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("image.png")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
- @Test
- public void testManyVisibleImagePreview_ScrollableImagePreview() {
- Uri uri = createTestContentProviderUri("image/png", null);
-
- 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().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.scrollable_image_preview))
- .perform(RecyclerViewActions.scrollToLastPosition())
- .check((view, exception) -> {
- if (exception != null) {
- throw exception;
- }
- RecyclerView recyclerView = (RecyclerView) view;
- assertThat(recyclerView.getAdapter().getItemCount(), is(uris.size()));
- });
- }
-
- @Test(timeout = 4_000)
- public void testPartiallyLoadedMetadata_previewIsShownForTheLoadedPart() {
- Uri imgOneUri = createTestContentProviderUri("image/png", null);
- Uri imgTwoUri = createTestContentProviderUri("image/png", null)
- .buildUpon()
- .path("image-2.png")
- .build();
- Uri docUri = createTestContentProviderUri("application/pdf", "image/png", 8_000);
- ArrayList<Uri> uris = new ArrayList<>(2);
- // two large previews to fill the screen and be presented right away and one
- // document that would be delayed by the URI metadata reading
- uris.add(imgOneUri);
- uris.add(imgTwoUri);
- uris.add(docUri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- Map<Uri, Bitmap> bitmaps = new HashMap<>();
- bitmaps.put(imgOneUri, createWideBitmap(Color.RED));
- bitmaps.put(imgTwoUri, createWideBitmap(Color.GREEN));
- bitmaps.put(docUri, createWideBitmap(Color.BLUE));
- ChooserActivityOverrideData.getInstance().imageLoader =
- new TestPreviewImageLoader(bitmaps);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos);
-
- // the preview type is expected to be resolved quickly based on the first provided URI
- // metadata. If, instead, it is dependent on the third URI metadata, the test should either
- // timeout or (more probably due to inner timeout) default to file preview type; anyway the
- // test will fail.
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(R.id.scrollable_image_preview))
- .check((view, exception) -> {
- if (exception != null) {
- throw exception;
- }
- RecyclerView recyclerView = (RecyclerView) view;
- assertThat(recyclerView.getChildCount()).isAtLeast(1);
- // the first view is a preview
- View imageView = recyclerView.getChildAt(0).findViewById(R.id.image);
- assertThat(imageView).isNotNull();
- })
- .perform(RecyclerViewActions.scrollToLastPosition())
- .check((view, exception) -> {
- if (exception != null) {
- throw exception;
- }
- RecyclerView recyclerView = (RecyclerView) view;
- assertThat(recyclerView.getChildCount()).isAtLeast(1);
- // check that the last view is a loading indicator
- View loadingIndicator =
- recyclerView.getChildAt(recyclerView.getChildCount() - 1);
- assertThat(loadingIndicator).isNotNull();
- });
- waitForIdle();
- }
-
- @Test
- public void testImageAndTextPreview() {
- final Uri uri = createTestContentProviderUri("image/png", null);
- 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().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withText(sharedText))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void test_shareImageWithRichText_RichTextIsDisplayed() {
- final Uri uri = createTestContentProviderUri("image/png", null);
- final CharSequence sharedText = new SpannableStringBuilder()
- .append(
- "text-",
- new StyleSpan(Typeface.BOLD_ITALIC),
- Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
- .append(
- Long.toString(System.currentTimeMillis()),
- new ForegroundColorSpan(Color.RED),
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withText(sharedText.toString()))
- .check(matches(isDisplayed()))
- .check((view, e) -> {
- if (e != null) {
- throw e;
- }
- assertThat(view).isInstanceOf(TextView.class);
- CharSequence text = ((TextView) view).getText();
- assertThat(text).isInstanceOf(Spanned.class);
- Spanned spanned = (Spanned) text;
- Object[] spans = spanned.getSpans(0, text.length(), Object.class);
- assertThat(spans).hasLength(2);
- assertThat(spanned.getSpans(0, 5, StyleSpan.class)).hasLength(1);
- assertThat(spanned.getSpans(5, text.length(), ForegroundColorSpan.class))
- .hasLength(1);
- });
- }
-
- @Test
- public void testTextPreviewWhenTextIsSharedWithMultipleImages() {
- final Uri uri = createTestContentProviderUri("image/png", null);
- final String sharedText = "text-" + System.currentTimeMillis();
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- Mockito.any(UserHandle.class)))
- .thenReturn(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);
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test"));
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- FakeEventLog.ChooserActivityShown event = eventLog.getChooserActivityShown();
- assertThat(event).isNotNull();
- assertThat(event.isWorkProfile()).isFalse();
- assertThat(event.getTargetMimeType()).isEqualTo(TEST_MIME_TYPE);
- }
-
- @Test
- public void testOnCreateLoggingFromWorkProfile() {
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
- ChooserActivityOverrideData.getInstance().alternateProfileSetting =
- MetricsEvent.MANAGED_PROFILE;
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test"));
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- FakeEventLog.ChooserActivityShown event = eventLog.getChooserActivityShown();
- assertThat(event).isNotNull();
- assertThat(event.isWorkProfile()).isTrue();
- assertThat(event.getTargetMimeType()).isEqualTo(TEST_MIME_TYPE);
- }
-
- @Test
- public void testEmptyPreviewLogging() {
- Intent sendIntent = createSendTextIntentWithPreview(null, null);
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent,
- "empty preview logger test"));
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- FakeEventLog.ChooserActivityShown event = eventLog.getChooserActivityShown();
- assertThat(event).isNotNull();
- assertThat(event.isWorkProfile()).isFalse();
- assertThat(event.getTargetMimeType()).isNull();
- }
-
- @Test
- public void testTitlePreviewLogging() {
- Intent sendIntent = createSendTextIntentWithPreview("TestTitle", null);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getActionShareWithPreview())
- .isEqualTo(new FakeEventLog.ActionShareWithPreview(
- /* previewType = */ CONTENT_PREVIEW_TEXT));
- }
-
- @Test
- public void testImagePreviewLogging() {
- Uri uri = createTestContentProviderUri("image/png", null);
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getActionShareWithPreview())
- .isEqualTo(new FakeEventLog.ActionShareWithPreview(
- /* previewType = */ CONTENT_PREVIEW_IMAGE));
- }
-
- @Test
- public void oneVisibleFilePreview() throws InterruptedException {
- Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
-
- @Test
- public void moreThanOneVisibleFilePreview() throws InterruptedException {
- Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
- uris.add(uri);
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
- onView(withId(R.id.content_preview_more_files)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_more_files)).check(matches(withText("+ 2 more files")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
- @Test
- public void contentProviderThrowSecurityException() throws InterruptedException {
- Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos);
-
- ChooserActivityOverrideData.getInstance().resolverForceException = true;
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
- @Test
- public void contentProviderReturnsNoColumns() throws InterruptedException {
- Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos);
-
- Cursor cursor = mock(Cursor.class);
- when(cursor.getCount()).thenReturn(1);
- Mockito.doNothing().when(cursor).close();
- when(cursor.moveToFirst()).thenReturn(true);
- when(cursor.getColumnIndex(Mockito.anyString())).thenReturn(-1);
-
- ChooserActivityOverrideData.getInstance().resolverCursor = cursor;
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
- onView(withId(R.id.content_preview_more_files)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_more_files)).check(matches(withText("+ 1 more file")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
- @Test
- public void testGetBaseScore() {
- final float testBaseScore = 0.89f;
-
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getScore(Mockito.isA(DisplayResolveInfo.class)))
- .thenReturn(testBaseScore);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- final DisplayResolveInfo testDri =
- activity.createTestDisplayResolveInfo(
- sendIntent,
- ResolverDataProvider.createResolveInfo(3, 0, PERSONAL_USER_HANDLE),
- "testLabel",
- "testInfo",
- sendIntent);
- final ChooserListAdapter adapter = activity.getAdapter();
-
- assertThat(adapter.getBaseScore(null, 0), is(CALLER_TARGET_SCORE_BOOST));
- assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_DEFAULT), is(testBaseScore));
- assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_CHOOSER_TARGET), is(testBaseScore));
- assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE),
- is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST));
- assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER),
- is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST));
- }
-
- // This test is too long and too slow and should not be taken as an example for future tests.
- @Test
- public void testDirectTargetSelectionLogging() {
- 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 =
- createShortcutLoaderFactory();
-
- // Start activity
- ChooserWrapperActivity activity =
- 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)).updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- List<ChooserTarget> serviceTargets = createDirectShareTargets(1, "");
- ShortcutLoader.Result result = new ShortcutLoader.Result(
- true,
- 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();
-
- final ChooserListAdapter activeAdapter = activity.getAdapter();
- assertThat(
- "Chooser should have 3 targets (2 apps, 1 direct)",
- activeAdapter.getCount(),
- is(3));
- assertThat(
- "Chooser should have exactly one selectable direct target",
- activeAdapter.getSelectableServiceTargetCount(),
- is(1));
- assertThat(
- "The resolver info must match the resolver info used to create the target",
- activeAdapter.getItem(0).getResolveInfo(),
- is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
-
- // Click on the direct target
- String name = serviceTargets.get(0).getTitle().toString();
- onView(withText(name))
- .perform(click());
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getShareTargetSelected()).hasSize(1);
- FakeEventLog.ShareTargetSelected call = eventLog.getShareTargetSelected().get(0);
- assertThat(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE);
- assertThat(call.getDirectTargetAlsoRanked()).isEqualTo(-1);
- var hashResult = call.getDirectTargetHashed();
- var hash = hashResult == null ? "" : hashResult.hashedString;
- assertWithMessage("Hash is not predictable but must be obfuscated")
- .that(hash).isNotEqualTo(name);
- }
-
- // This test is too long and too slow and should not be taken as an example for future tests.
- @Test
- public void testDirectTargetLoggingWithRankedAppTarget() {
- 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 =
- createShortcutLoaderFactory();
-
- // Start activity
- ChooserWrapperActivity activity =
- 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)).updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- List<ChooserTarget> serviceTargets = createDirectShareTargets(
- 1,
- resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
- ShortcutLoader.Result result = new ShortcutLoader.Result(
- true,
- 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();
-
- final ChooserListAdapter activeAdapter = activity.getAdapter();
- assertThat(
- "Chooser should have 3 targets (2 apps, 1 direct)",
- activeAdapter.getCount(),
- is(3));
- assertThat(
- "Chooser should have exactly one selectable direct target",
- activeAdapter.getSelectableServiceTargetCount(),
- is(1));
- assertThat(
- "The resolver info must match the resolver info used to create the target",
- activeAdapter.getItem(0).getResolveInfo(),
- is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
-
- // Click on the direct target
- String name = serviceTargets.get(0).getTitle().toString();
- onView(withText(name))
- .perform(click());
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getShareTargetSelected()).hasSize(1);
- FakeEventLog.ShareTargetSelected call = eventLog.getShareTargetSelected().get(0);
-
- assertThat(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE);
- assertThat(call.getDirectTargetAlsoRanked()).isEqualTo(0);
- }
-
- @Test
- public void testShortcutTargetWithApplyAppLimits() {
- // Set up resources
- Resources resources = Mockito.spy(
- InstrumentationRegistry.getInstrumentation().getContext().getResources());
- ChooserActivityOverrideData.getInstance().resources = resources;
- doReturn(1).when(resources).getInteger(R.integer.config_maxShortcutTargetsPerApp);
- 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 =
- createShortcutLoaderFactory();
-
- // 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)).updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- List<ChooserTarget> serviceTargets = createDirectShareTargets(
- 2,
- resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
- ShortcutLoader.Result result = new ShortcutLoader.Result(
- true,
- 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();
-
- final ChooserListAdapter activeAdapter = activity.getAdapter();
- assertThat(
- "Chooser should have 3 targets (2 apps, 1 direct)",
- activeAdapter.getCount(),
- is(3));
- assertThat(
- "Chooser should have exactly one selectable direct target",
- activeAdapter.getSelectableServiceTargetCount(),
- is(1));
- assertThat(
- "The resolver info must match the resolver info used to create the target",
- activeAdapter.getItem(0).getResolveInfo(),
- is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
- assertThat(
- "The display label must match",
- activeAdapter.getItem(0).getDisplayLabel(),
- is("testTitle0"));
- }
-
- @Test
- public void testShortcutTargetWithoutApplyAppLimits() {
- setDeviceConfigProperty(
- SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
- Boolean.toString(false));
- // Set up resources
- Resources resources = Mockito.spy(
- InstrumentationRegistry.getInstrumentation().getContext().getResources());
- ChooserActivityOverrideData.getInstance().resources = resources;
- doReturn(1).when(resources).getInteger(R.integer.config_maxShortcutTargetsPerApp);
- 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 =
- createShortcutLoaderFactory();
-
- // 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)).updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- List<ChooserTarget> serviceTargets = createDirectShareTargets(
- 2,
- resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
- ShortcutLoader.Result result = new ShortcutLoader.Result(
- true,
- 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();
-
- final ChooserListAdapter activeAdapter = activity.getAdapter();
- assertThat(
- "Chooser should have 4 targets (2 apps, 2 direct)",
- activeAdapter.getCount(),
- is(4));
- assertThat(
- "Chooser should have exactly two selectable direct target",
- activeAdapter.getSelectableServiceTargetCount(),
- is(2));
- assertThat(
- "The resolver info must match the resolver info used to create the target",
- activeAdapter.getItem(0).getResolveInfo(),
- is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
- assertThat(
- "The display label must match",
- activeAdapter.getItem(0).getDisplayLabel(),
- is("testTitle0"));
- assertThat(
- "The display label must match",
- activeAdapter.getItem(1).getDisplayLabel(),
- is("testTitle1"));
- }
-
- @Test
- public void testLaunchWithCallerProvidedTarget() {
- setDeviceConfigProperty(
- SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
- Boolean.toString(false));
- // Set up resources
- Resources resources = Mockito.spy(
- InstrumentationRegistry.getInstrumentation().getContext().getResources());
- ChooserActivityOverrideData.getInstance().resources = resources;
- doReturn(1).when(resources).getInteger(R.integer.config_maxShortcutTargetsPerApp);
-
- // We need app targets for direct targets to get displayed
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos, resolvedComponentInfos);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- // set caller-provided target
- Intent chooserIntent = Intent.createChooser(createSendTextIntent(), null);
- String callerTargetLabel = "Caller Target";
- ChooserTarget[] targets = new ChooserTarget[] {
- new ChooserTarget(
- callerTargetLabel,
- Icon.createWithBitmap(createBitmap()),
- 0.1f,
- resolvedComponentInfos.get(0).name,
- new Bundle())
- };
- chooserIntent.putExtra(Intent.EXTRA_CHOOSER_TARGETS, targets);
-
- // create test shortcut loader factory, remember loaders and their callbacks
- SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders =
- createShortcutLoaderFactory();
-
- // Start activity
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
-
- // verify that ShortcutLoader was queried
- ArgumentCaptor<DisplayResolveInfo[]> appTargets =
- ArgumentCaptor.forClass(DisplayResolveInfo[].class);
- verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- ShortcutLoader.Result result = new ShortcutLoader.Result(
- true,
- appTargets.getValue(),
- new ShortcutLoader.ShortcutResultInfo[0],
- new HashMap<>(),
- new HashMap<>());
- activity.getMainExecutor().execute(() -> shortcutLoaders.get(0).second.accept(result));
- waitForIdle();
-
- final ChooserListAdapter activeAdapter = activity.getAdapter();
- assertThat(
- "Chooser should have 3 targets (2 apps, 1 direct)",
- activeAdapter.getCount(),
- is(3));
- assertThat(
- "Chooser should have exactly two selectable direct target",
- activeAdapter.getSelectableServiceTargetCount(),
- is(1));
- assertThat(
- "The display label must match",
- activeAdapter.getItem(0).getDisplayLabel(),
- is(callerTargetLabel));
-
- // Switch to work profile and ensure that the target *doesn't* show up there.
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- for (int i = 0; i < activity.getWorkListAdapter().getCount(); i++) {
- assertThat(
- "Chooser target should not show up in opposite profile",
- activity.getWorkListAdapter().getItem(i).getDisplayLabel(),
- not(callerTargetLabel));
- }
- }
-
- @Test
- 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),
- Context.RECEIVER_EXPORTED);
-
- try {
- onView(withText(customActionLabel)).perform(click());
- assertTrue("Timeout waiting for broadcast",
- broadcastInvoked.await(5000, TimeUnit.MILLISECONDS));
- } finally {
- testContext.unregisterReceiver(testReceiver);
- }
- }
-
- @Test
- 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);
- String label = "modify share";
- PendingIntent pendingIntent = PendingIntent.getBroadcast(
- testContext,
- 123,
- new Intent(modifyShareAction),
- PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
- ChooserAction action = new ChooserAction.Builder(Icon.createWithBitmap(
- createBitmap()), label, pendingIntent).build();
- chooserIntent.putExtra(
- Intent.EXTRA_CHOOSER_MODIFY_SHARE_ACTION,
- action);
- // 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),
- Context.RECEIVER_EXPORTED);
-
- try {
- onView(withText(label)).perform(click());
- assertTrue("Timeout waiting for broadcast",
- broadcastInvoked.await(5000, TimeUnit.MILLISECONDS));
-
- } finally {
- testContext.unregisterReceiver(testReceiver);
- }
- }
-
- @Test
- public void testUpdateMaxTargetsPerRow_columnCountIsUpdated() throws InterruptedException {
- updateMaxTargetsPerRowResource(/* targetsPerRow= */ 4);
- givenAppTargets(/* appCount= */ 16);
- Intent sendIntent = createSendTextIntent();
- final ChooserActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
-
- updateMaxTargetsPerRowResource(/* targetsPerRow= */ 6);
- InstrumentationRegistry.getInstrumentation()
- .runOnMainSync(() -> activity.onConfigurationChanged(
- InstrumentationRegistry.getInstrumentation()
- .getContext().getResources().getConfiguration()));
-
- waitForIdle();
- onView(withId(com.android.internal.R.id.resolver_list))
- .check(matches(withGridColumnCount(6)));
- }
-
- // This test is too long and too slow and should not be taken as an example for future tests.
- @Test @Ignore
- public void testDirectTargetLoggingWithAppTargetNotRankedPortrait()
- throws InterruptedException {
- testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_PORTRAIT, 4);
- }
-
- @Test @Ignore
- public void testDirectTargetLoggingWithAppTargetNotRankedLandscape()
- throws InterruptedException {
- testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_LANDSCAPE, 8);
- }
-
- private void testDirectTargetLoggingWithAppTargetNotRanked(
- int orientation, int appTargetsExpected) {
- Configuration configuration =
- new Configuration(InstrumentationRegistry.getInstrumentation().getContext()
- .getResources().getConfiguration());
- configuration.orientation = orientation;
-
- Resources resources = Mockito.spy(
- InstrumentationRegistry.getInstrumentation().getContext().getResources());
- ChooserActivityOverrideData.getInstance().resources = resources;
- doReturn(configuration).when(resources).getConfiguration();
-
- Intent sendIntent = createSendTextIntent();
- // We need app targets for direct targets to get displayed
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(15);
- setupResolverControllers(resolvedComponentInfos);
-
- // Create direct share target
- List<ChooserTarget> serviceTargets = createDirectShareTargets(1,
- resolvedComponentInfos.get(14).getResolveInfoAt(0).activityInfo.packageName);
- ResolveInfo ri = ResolverDataProvider.createResolveInfo(16, 0, PERSONAL_USER_HANDLE);
-
- // Start activity
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- // Insert the direct share target
- Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
- directShareToShortcutInfos.put(serviceTargets.get(0), null);
- InstrumentationRegistry.getInstrumentation().runOnMainSync(
- () -> activity.getAdapter().addServiceResults(
- activity.createTestDisplayResolveInfo(sendIntent,
- ri,
- "testLabel",
- "testInfo",
- sendIntent),
- serviceTargets,
- TARGET_TYPE_CHOOSER_TARGET,
- directShareToShortcutInfos,
- /* directShareToAppTargets */ null)
- );
-
- assertThat(
- String.format("Chooser should have %d targets (%d apps, 1 direct, 15 A-Z)",
- appTargetsExpected + 16, appTargetsExpected),
- activity.getAdapter().getCount(), is(appTargetsExpected + 16));
- assertThat("Chooser should have exactly one selectable direct target",
- activity.getAdapter().getSelectableServiceTargetCount(), is(1));
- assertThat("The resolver info must match the resolver info used to create the target",
- activity.getAdapter().getItem(0).getResolveInfo(), is(ri));
-
- // Click on the direct target
- String name = serviceTargets.get(0).getTitle().toString();
- onView(withText(name))
- .perform(click());
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- var invocations = eventLog.getShareTargetSelected();
- assertWithMessage("Only one ShareTargetSelected event logged")
- .that(invocations).hasSize(1);
- FakeEventLog.ShareTargetSelected call = invocations.get(0);
- assertWithMessage("targetType should be SELECTION_TYPE_SERVICE")
- .that(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE);
- assertWithMessage(
- "The packages shouldn't match for app target and direct target")
- .that(call.getDirectTargetAlsoRanked()).isEqualTo(-1);
- }
-
- @Test
- public void testWorkTab_displayedWhenWorkProfileUserAvailable() {
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
-
- onView(withId(android.R.id.tabs)).check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_hiddenWhenWorkProfileUserNotAvailable() {
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
-
- onView(withId(android.R.id.tabs)).check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testWorkTab_eachTabUsesExpectedAdapter() {
- int personalProfileTargets = 3;
- int otherProfileTargets = 1;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(
- personalProfileTargets + otherProfileTargets, /* userID */ 10);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(
- workProfileTargets);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
-
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
- onView(withText(R.string.resolver_work_tab)).perform(click());
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
- assertThat(activity.getPersonalListAdapter().getCount(), is(personalProfileTargets));
- assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets));
- }
-
- @Test
- public void testWorkTab_workProfileHasExpectedNumberOfTargets() throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets));
- }
-
- @Test @Ignore
- public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(first(allOf(
- withText(workResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name),
- isDisplayed())))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
- }
-
- @Test
- public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
-
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_workProfileDisabled_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- ChooserActivityOverrideData.getInstance().isQuietModeEnabled = true;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_turn_on_work_apps))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_noWorkAppsAvailable_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_no_work_apps_available))
- .check(matches(isDisplayed()));
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_SCROLLABLE_PREVIEW)
- public void testWorkTab_previewIsScrollable() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(300);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
-
- Uri uri = createTestContentProviderUri("image/png", null);
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createWideBitmap());
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "Scrollable preview test"));
- waitForIdle();
-
- onView(withId(com.android.intentresolver.R.id.scrollable_image_preview))
- .check(matches(isDisplayed()));
-
- onView(withId(com.android.internal.R.id.contentPanel)).perform(swipeUp());
- waitForIdle();
-
- onView(withId(com.android.intentresolver.R.id.chooser_headline_row_container))
- .check(matches(isCompletelyDisplayed()));
- onView(withId(com.android.intentresolver.R.id.headline))
- .check(matches(isDisplayed()));
- onView(withId(com.android.intentresolver.R.id.scrollable_image_preview))
- .check(matches(not(isDisplayed())));
- }
-
- @Ignore // b/220067877
- @Test
- public void testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- ChooserActivityOverrideData.getInstance().isQuietModeEnabled = true;
- ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- ChooserActivityOverrideData.getInstance().isQuietModeEnabled = true;
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_no_work_apps_available))
- .check(matches(isDisplayed()));
- }
-
- @Test @Ignore("b/222124533")
- public void testAppTargetLogging() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- // TODO(b/222124533): other test cases use a timeout to make sure that the UI is fully
- // populated; without one, this test flakes. Ideally we should address the need for a
- // timeout everywhere instead of introducing one to fix this particular test.
-
- assertThat(activity.getAdapter().getCount(), is(2));
- onView(withId(com.android.internal.R.id.profile_button)).check(doesNotExist());
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
- onView(withText(toChoose.activityInfo.name))
- .perform(click());
- waitForIdle();
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
- @Test
- public void testDirectTargetLogging() {
- 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
- ChooserWrapperActivity activity =
- 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))
- .updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- 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();
-
- assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
- activity.getAdapter().getCount(), is(3));
- assertThat("Chooser should have exactly one selectable direct target",
- activity.getAdapter().getSelectableServiceTargetCount(), is(1));
- assertThat(
- "The resolver info must match the resolver info used to create the target",
- activity.getAdapter().getItem(0).getResolveInfo(),
- is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
-
- // Click on the direct target
- String name = serviceTargets.get(0).getTitle().toString();
- onView(withText(name))
- .perform(click());
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getShareTargetSelected()).hasSize(1);
- FakeEventLog.ShareTargetSelected call = eventLog.getShareTargetSelected().get(0);
- assertThat(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE);
- }
-
- @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))
- .updateAppTargets(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);
- setupResolverControllers(resolvedComponentInfos);
-
- // Start activity
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
-
- // Thread.sleep shouldn't be a thing in an integration test but it's
- // necessary here because of the way the code is structured
- Thread.sleep(3000);
-
- assertThat("Chooser should have 2 app targets",
- activity.getAdapter().getCount(), is(2));
- assertThat("Chooser should have no direct targets",
- activity.getAdapter().getSelectableServiceTargetCount(), is(0));
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
- @Ignore // b/220067877
- @Test
- public void testCopyTextToClipboardLogging() throws Exception {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.chooser_copy_button)).check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.chooser_copy_button)).perform(click());
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
- @Test @Ignore("b/222124533")
- public void testSwitchProfileLogging() throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
- onView(withText(R.string.resolver_personal_tab)).perform(click());
- waitForIdle();
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
- @Test
- public void testWorkTab_onePersonalTarget_emptyStateOnWorkTarget_doesNotAutoLaunch() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "Test"));
- waitForIdle();
-
- assertNull(chosen[0]);
- }
-
- @Test
- public void testOneInitialIntent_noAutolaunch() {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(1);
- setupResolverControllers(personalResolvedComponentInfos);
- Intent chooserIntent = createChooserIntent(createSendTextIntent(),
- new Intent[] {new Intent("action.fake")});
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
- ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
- ResolveInfo ri = createFakeResolveInfo();
- when(
- ChooserActivityOverrideData
- .getInstance().packageManager
- .resolveActivity(any(Intent.class), any()))
- .thenReturn(ri);
- waitForIdle();
-
- IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
-
- assertNull(chosen[0]);
- assertThat(activity
- .getPersonalListAdapter().getCallerTargetCount(), is(1));
- }
-
- @Test
- public void testWorkTab_withInitialIntents_workTabDoesNotIncludePersonalInitialIntents() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 1;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent[] initialIntents = {
- new Intent("action.fake1"),
- new Intent("action.fake2")
- };
- Intent chooserIntent = createChooserIntent(createSendTextIntent(), initialIntents);
- ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .packageManager
- .resolveActivity(any(Intent.class), any()))
- .thenReturn(createFakeResolveInfo());
- waitForIdle();
-
- IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
-
- assertThat(activity.getPersonalListAdapter().getCallerTargetCount(), is(2));
- assertThat(activity.getWorkListAdapter().getCallerTargetCount(), is(0));
- }
-
- @Test
- public void testWorkTab_xProfileIntentsDisabled_personalToWork_nonSendIntent_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent[] initialIntents = {
- new Intent("action.fake1"),
- new Intent("action.fake2")
- };
- Intent chooserIntent = createChooserIntent(new Intent(), initialIntents);
- ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .packageManager
- .resolveActivity(any(Intent.class), any()))
- .thenReturn(createFakeResolveInfo());
-
- mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
-
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_noWorkAppsAvailable_nonSendIntent_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent[] initialIntents = {
- new Intent("action.fake1"),
- new Intent("action.fake2")
- };
- Intent chooserIntent = createChooserIntent(new Intent(), initialIntents);
- ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .packageManager
- .resolveActivity(any(Intent.class), any()))
- .thenReturn(createFakeResolveInfo());
-
- mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_no_work_apps_available))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testDeduplicateCallerTargetRankedTarget() {
- // Create 4 ranked app targets.
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(4);
- setupResolverControllers(personalResolvedComponentInfos);
- // Create caller target which is duplicate with one of app targets
- Intent chooserIntent = createChooserIntent(createSendTextIntent(),
- new Intent[] {new Intent("action.fake")});
- ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
- ResolveInfo ri = ResolverDataProvider.createResolveInfo(0,
- UserHandle.USER_CURRENT, PERSONAL_USER_HANDLE);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .packageManager
- .resolveActivity(any(Intent.class), any()))
- .thenReturn(ri);
- waitForIdle();
-
- IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
-
- // Total 4 targets (1 caller target, 3 ranked targets)
- assertThat(activity.getAdapter().getCount(), is(4));
- assertThat(activity.getAdapter().getCallerTargetCount(), is(1));
- assertThat(activity.getAdapter().getRankedTargetCount(), is(3));
- }
-
- @Test
- public void test_query_shortcut_loader_for_the_selected_tab() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- ShortcutLoader personalProfileShortcutLoader = mock(ShortcutLoader.class);
- ShortcutLoader workProfileShortcutLoader = mock(ShortcutLoader.class);
- final SparseArray<ShortcutLoader> shortcutLoaders = new SparseArray<>();
- shortcutLoaders.put(0, personalProfileShortcutLoader);
- shortcutLoaders.put(10, workProfileShortcutLoader);
- ChooserActivityOverrideData.getInstance().shortcutLoaderFactory =
- (userHandle, callback) -> shortcutLoaders.get(userHandle.getIdentifier(), null);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- waitForIdle();
-
- verify(personalProfileShortcutLoader, times(1)).updateAppTargets(any());
-
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- verify(workProfileShortcutLoader, times(1)).updateAppTargets(any());
- }
-
- @Test
- public void testClonedProfilePresent_personalAdapterIsSetWithPersonalProfile() {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ false, /* cloneAvailable= */ true);
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- setupResolverControllers(resolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
-
- final IChooserWrapper activity = (IChooserWrapper) mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, "personalProfileTest"));
- waitForIdle();
-
- assertThat(activity.getPersonalListAdapter().getUserHandle(), is(PERSONAL_USER_HANDLE));
- assertThat(activity.getAdapter().getCount(), is(3));
- }
-
- @Test
- public void testClonedProfilePresent_personalTabUsesExpectedAdapter() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ true);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(
- 4);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "multi tab test"));
- waitForIdle();
-
- assertThat(activity.getCurrentUserHandle(), is(PERSONAL_USER_HANDLE));
- }
-
- private Intent createChooserIntent(Intent intent, Intent[] initialIntents) {
- Intent chooserIntent = new Intent();
- chooserIntent.setAction(Intent.ACTION_CHOOSER);
- chooserIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
- chooserIntent.putExtra(Intent.EXTRA_TITLE, "some title");
- chooserIntent.putExtra(Intent.EXTRA_INTENT, intent);
- chooserIntent.setType("text/plain");
- if (initialIntents != null) {
- chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, initialIntents);
- }
- return chooserIntent;
- }
-
- /* This is a "test of a test" to make sure that our inherited test class
- * is successfully configured to operate on the unbundled-equivalent
- * ChooserWrapperActivity.
- *
- * TODO: remove after unbundling is complete.
- */
- @Test
- public void testWrapperActivityHasExpectedConcreteType() {
- final ChooserActivity activity = mActivityRule.launchActivity(
- Intent.createChooser(new Intent("ACTION_FOO"), "foo"));
- waitForIdle();
- assertThat(activity).isInstanceOf(ChooserWrapperActivity.class);
- }
-
- private ResolveInfo createFakeResolveInfo() {
- ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.name = "FakeActivityName";
- ri.activityInfo.packageName = "fake.package.name";
- ri.activityInfo.applicationInfo = new ApplicationInfo();
- ri.activityInfo.applicationInfo.packageName = "fake.package.name";
- ri.userHandle = UserHandle.CURRENT;
- return ri;
- }
-
- private Intent createSendTextIntent() {
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
- sendIntent.setType("text/plain");
- return sendIntent;
- }
-
- private Intent createSendImageIntent(Uri imageThumbnail) {
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_STREAM, imageThumbnail);
- sendIntent.setType("image/png");
- if (imageThumbnail != null) {
- ClipData.Item clipItem = new ClipData.Item(imageThumbnail);
- sendIntent.setClipData(new ClipData("Clip Label", new String[]{"image/png"}, clipItem));
- }
-
- return sendIntent;
- }
-
- private Uri createTestContentProviderUri(
- @Nullable String mimeType, @Nullable String streamType) {
- return createTestContentProviderUri(mimeType, streamType, 0);
- }
-
- private Uri createTestContentProviderUri(
- @Nullable String mimeType, @Nullable String streamType, long streamTypeTimeout) {
- String packageName =
- InstrumentationRegistry.getInstrumentation().getContext().getPackageName();
- Uri.Builder builder = Uri.parse("content://" + packageName + "/image.png")
- .buildUpon();
- if (mimeType != null) {
- builder.appendQueryParameter(TestContentProvider.PARAM_MIME_TYPE, mimeType);
- }
- if (streamType != null) {
- builder.appendQueryParameter(TestContentProvider.PARAM_STREAM_TYPE, streamType);
- }
- if (streamTypeTimeout > 0) {
- builder.appendQueryParameter(
- TestContentProvider.PARAM_STREAM_TYPE_TIMEOUT,
- Long.toString(streamTypeTimeout));
- }
- return builder.build();
- }
-
- private Intent createSendTextIntentWithPreview(String title, Uri imageThumbnail) {
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
- sendIntent.putExtra(Intent.EXTRA_TITLE, title);
- if (imageThumbnail != null) {
- ClipData.Item clipItem = new ClipData.Item(imageThumbnail);
- sendIntent.setClipData(new ClipData("Clip Label", new String[]{"image/png"}, clipItem));
- }
-
- return sendIntent;
- }
-
- private Intent createSendUriIntentWithPreview(ArrayList<Uri> uris) {
- Intent sendIntent = new Intent();
-
- if (uris.size() > 1) {
- sendIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
- sendIntent.putExtra(Intent.EXTRA_STREAM, uris);
- } else {
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_STREAM, uris.get(0));
- }
-
- return sendIntent;
- }
-
- private Intent createViewTextIntent() {
- Intent viewIntent = new Intent();
- viewIntent.setAction(Intent.ACTION_VIEW);
- viewIntent.putExtra(Intent.EXTRA_TEXT, "testing intent viewing");
- return viewIntent;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, PERSONAL_USER_HANDLE));
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsWithCloneProfileForTest(
- int numberOfResults,
- UserHandle resolvedForPersonalUser,
- UserHandle resolvedForClonedUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < 1; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- resolvedForPersonalUser));
- }
- for (int i = 1; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- resolvedForClonedUser));
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
- int numberOfResults) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- if (i == 0) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i,
- PERSONAL_USER_HANDLE));
- } else {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- PERSONAL_USER_HANDLE));
- }
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
- int numberOfResults, int userId) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- if (i == 0) {
- infoList.add(
- ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId,
- PERSONAL_USER_HANDLE));
- } else {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- PERSONAL_USER_HANDLE));
- }
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithUserId(
- int numberOfResults, int userId) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId,
- PERSONAL_USER_HANDLE));
- }
- return infoList;
- }
-
- private List<ChooserTarget> createDirectShareTargets(int numberOfResults, String packageName) {
- Icon icon = Icon.createWithBitmap(createBitmap());
- String testTitle = "testTitle";
- List<ChooserTarget> targets = new ArrayList<>();
- for (int i = 0; i < numberOfResults; i++) {
- ComponentName componentName;
- if (packageName.isEmpty()) {
- componentName = ResolverDataProvider.createComponentName(i);
- } else {
- componentName = new ComponentName(packageName, packageName + ".class");
- }
- ChooserTarget tempTarget = new ChooserTarget(
- testTitle + i,
- icon,
- (float) (1 - ((i + 1) / 10.0)),
- componentName,
- null);
- targets.add(tempTarget);
- }
- return targets;
- }
-
- private void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- private Bitmap createBitmap() {
- return createBitmap(200, 200);
- }
-
- private Bitmap createWideBitmap() {
- return createWideBitmap(Color.RED);
- }
-
- private Bitmap createWideBitmap(int bgColor) {
- WindowManager windowManager = InstrumentationRegistry.getInstrumentation()
- .getTargetContext()
- .getSystemService(WindowManager.class);
- int width = 3000;
- if (windowManager != null) {
- Rect bounds = windowManager.getMaximumWindowMetrics().getBounds();
- width = bounds.width() + 200;
- }
- return createBitmap(width, 100, bgColor);
- }
-
- private Bitmap createBitmap(int width, int height) {
- return createBitmap(width, height, Color.RED);
- }
-
- private Bitmap createBitmap(int width, int height, int bgColor) {
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
-
- Paint paint = new Paint();
- paint.setColor(bgColor);
- paint.setStyle(Paint.Style.FILL);
- canvas.drawPaint(paint);
-
- paint.setColor(Color.WHITE);
- paint.setAntiAlias(true);
- paint.setTextSize(14.f);
- paint.setTextAlign(Paint.Align.CENTER);
- canvas.drawText("Hi!", (width / 2.f), (height / 2.f), paint);
-
- return bitmap;
- }
-
- private List<ShareShortcutInfo> createShortcuts(Context context) {
- Intent testIntent = new Intent("TestIntent");
-
- List<ShareShortcutInfo> shortcuts = new ArrayList<>();
- shortcuts.add(new ShareShortcutInfo(
- new ShortcutInfo.Builder(context, "shortcut1")
- .setIntent(testIntent).setShortLabel("label1").setRank(3).build(), // 0 2
- new ComponentName("package1", "class1")));
- shortcuts.add(new ShareShortcutInfo(
- new ShortcutInfo.Builder(context, "shortcut2")
- .setIntent(testIntent).setShortLabel("label2").setRank(7).build(), // 1 3
- new ComponentName("package2", "class2")));
- shortcuts.add(new ShareShortcutInfo(
- new ShortcutInfo.Builder(context, "shortcut3")
- .setIntent(testIntent).setShortLabel("label3").setRank(1).build(), // 2 0
- new ComponentName("package3", "class3")));
- shortcuts.add(new ShareShortcutInfo(
- new ShortcutInfo.Builder(context, "shortcut4")
- .setIntent(testIntent).setShortLabel("label4").setRank(3).build(), // 3 2
- new ComponentName("package4", "class4")));
-
- return shortcuts;
- }
-
- private void markOtherProfileAvailability(boolean workAvailable, boolean cloneAvailable) {
- AnnotatedUserHandles.Builder handles = AnnotatedUserHandles.newBuilder();
- handles
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(PERSONAL_USER_HANDLE)
- .setPersonalProfileUserHandle(PERSONAL_USER_HANDLE);
- if (workAvailable) {
- handles.setWorkProfileUserHandle(WORK_PROFILE_USER_HANDLE);
- }
- if (cloneAvailable) {
- handles.setCloneProfileUserHandle(CLONE_PROFILE_USER_HANDLE);
- }
- ChooserWrapperActivity.sOverrides.annotatedUserHandles = handles.build();
- }
-
- private void setupResolverControllers(
- List<ResolvedComponentInfo> personalResolvedComponentInfos) {
- setupResolverControllers(personalResolvedComponentInfos, new ArrayList<>());
- }
-
- private void setupResolverControllers(
- List<ResolvedComponentInfo> personalResolvedComponentInfos,
- List<ResolvedComponentInfo> workResolvedComponentInfos) {
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(
- ChooserActivityOverrideData
- .getInstance()
- .workResolverListController
- .getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(
- ChooserActivityOverrideData
- .getInstance()
- .workResolverListController
- .getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.of(10))))
- .thenReturn(new ArrayList<>(workResolvedComponentInfos));
- }
-
- private static GridRecyclerSpanCountMatcher withGridColumnCount(int columnCount) {
- return new GridRecyclerSpanCountMatcher(Matchers.is(columnCount));
- }
-
- private static class GridRecyclerSpanCountMatcher extends
- BoundedDiagnosingMatcher<View, RecyclerView> {
-
- private final Matcher<Integer> mIntegerMatcher;
-
- private GridRecyclerSpanCountMatcher(Matcher<Integer> integerMatcher) {
- super(RecyclerView.class);
- this.mIntegerMatcher = integerMatcher;
- }
-
- @Override
- protected void describeMoreTo(Description description) {
- description.appendText("RecyclerView grid layout span count to match: ");
- this.mIntegerMatcher.describeTo(description);
- }
-
- @Override
- protected boolean matchesSafely(RecyclerView view, Description mismatchDescription) {
- int spanCount = ((GridLayoutManager) view.getLayoutManager()).getSpanCount();
- if (this.mIntegerMatcher.matches(spanCount)) {
- return true;
- } else {
- mismatchDescription.appendText("RecyclerView grid layout span count was ")
- .appendValue(spanCount);
- return false;
- }
- }
- }
-
- private void givenAppTargets(int appCount) {
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTest(appCount);
- setupResolverControllers(resolvedComponentInfos);
- }
-
- private void updateMaxTargetsPerRowResource(int targetsPerRow) {
- Resources resources = Mockito.spy(
- InstrumentationRegistry.getInstrumentation().getContext().getResources());
- ChooserActivityOverrideData.getInstance().resources = resources;
- doReturn(targetsPerRow).when(resources).getInteger(
- R.integer.config_chooser_max_targets_per_row);
- }
-
- private SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>>
- createShortcutLoaderFactory() {
- 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;
- };
- return shortcutLoaders;
- }
-
- private static ImageLoader createImageLoader(Uri uri, Bitmap bitmap) {
- return new TestPreviewImageLoader(Collections.singletonMap(uri, bitmap));
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java
deleted file mode 100644
index da879f74..00000000
--- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityWorkProfileTest.java
+++ /dev/null
@@ -1,480 +0,0 @@
-/*
- * 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 static android.testing.PollingCheck.waitFor;
-
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.action.ViewActions.swipeUp;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-
-import static com.android.intentresolver.ChooserWrapperActivity.sOverrides;
-import static com.android.intentresolver.UnbundledChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.NO_BLOCKER;
-import static com.android.intentresolver.UnbundledChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_ACCESS_BLOCKER;
-import static com.android.intentresolver.UnbundledChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_SHARE_BLOCKER;
-import static com.android.intentresolver.UnbundledChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_ACCESS_BLOCKER;
-import static com.android.intentresolver.UnbundledChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_SHARE_BLOCKER;
-import static com.android.intentresolver.UnbundledChooserActivityWorkProfileTest.TestCase.Tab.PERSONAL;
-import static com.android.intentresolver.UnbundledChooserActivityWorkProfileTest.TestCase.Tab.WORK;
-
-import static org.hamcrest.CoreMatchers.not;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-
-import android.companion.DeviceFilter;
-import android.content.Intent;
-import android.os.UserHandle;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.espresso.NoMatchingViewException;
-import androidx.test.rule.ActivityTestRule;
-
-import com.android.intentresolver.UnbundledChooserActivityWorkProfileTest.TestCase.Tab;
-
-import junit.framework.AssertionFailedError;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-import dagger.hilt.android.testing.HiltAndroidRule;
-import dagger.hilt.android.testing.HiltAndroidTest;
-
-@DeviceFilter.MediumType
-@RunWith(Parameterized.class)
-@HiltAndroidTest
-public class UnbundledChooserActivityWorkProfileTest {
-
- private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
- .getInstrumentation().getTargetContext().getUser();
- private static final UserHandle WORK_USER_HANDLE = UserHandle.of(10);
-
- @Rule(order = 0)
- public HiltAndroidRule mHiltAndroidRule = new HiltAndroidRule(this);
-
- @Rule(order = 1)
- public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
- new ActivityTestRule<>(ChooserWrapperActivity.class, false,
- false);
- private final TestCase mTestCase;
-
- public UnbundledChooserActivityWorkProfileTest(TestCase testCase) {
- mTestCase = testCase;
- }
-
- @Before
- public void cleanOverrideData() {
- // TODO: use the other form of `adoptShellPermissionIdentity()` where we explicitly list the
- // permissions we require (which we'll read from the manifest at runtime).
- InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .adoptShellPermissionIdentity();
-
- sOverrides.reset();
- }
-
- @Test
- public void testBlocker() {
- setUpPersonalAndWorkComponentInfos();
- sOverrides.hasCrossProfileIntents = mTestCase.hasCrossProfileIntents();
-
- launchActivity(mTestCase.getIsSendAction());
- switchToTab(mTestCase.getTab());
-
- switch (mTestCase.getExpectedBlocker()) {
- case NO_BLOCKER:
- assertNoBlockerDisplayed();
- break;
- case PERSONAL_PROFILE_SHARE_BLOCKER:
- assertCantSharePersonalAppsBlockerDisplayed();
- break;
- case WORK_PROFILE_SHARE_BLOCKER:
- assertCantShareWorkAppsBlockerDisplayed();
- break;
- case PERSONAL_PROFILE_ACCESS_BLOCKER:
- assertCantAccessPersonalAppsBlockerDisplayed();
- break;
- case WORK_PROFILE_ACCESS_BLOCKER:
- assertCantAccessWorkAppsBlockerDisplayed();
- break;
- }
- }
-
- @Parameterized.Parameters(name = "{0}")
- public static Collection tests() {
- return Arrays.asList(
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ WORK_PROFILE_SHARE_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ PERSONAL_PROFILE_SHARE_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ WORK_PROFILE_ACCESS_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ PERSONAL_PROFILE_ACCESS_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- )
- );
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
- int numberOfResults, int userId, UserHandle resolvedForUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- infoList.add(
- ResolverDataProvider
- .createResolvedComponentInfoWithOtherId(i, userId, resolvedForUser));
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults,
- UserHandle resolvedForUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, resolvedForUser));
- }
- return infoList;
- }
-
- private void setUpPersonalAndWorkComponentInfos() {
- ChooserWrapperActivity.sOverrides.annotatedUserHandles = AnnotatedUserHandles.newBuilder()
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(mTestCase.getMyUserHandle())
- .setPersonalProfileUserHandle(PERSONAL_USER_HANDLE)
- .setWorkProfileUserHandle(WORK_USER_HANDLE)
- .build();
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3,
- /* userId */ WORK_USER_HANDLE.getIdentifier(), PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets, WORK_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- }
-
- private void setupResolverControllers(
- List<ResolvedComponentInfo> personalResolvedComponentInfos,
- List<ResolvedComponentInfo> workResolvedComponentInfos) {
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .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() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- private void assertCantAccessWorkAppsBlockerDisplayed() {
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- onView(withText(R.string.resolver_cant_access_work_apps_explanation))
- .check(matches(isDisplayed()));
- }
-
- private void assertCantAccessPersonalAppsBlockerDisplayed() {
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- onView(withText(R.string.resolver_cant_access_personal_apps_explanation))
- .check(matches(isDisplayed()));
- }
-
- private void assertCantShareWorkAppsBlockerDisplayed() {
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- onView(withText(R.string.resolver_cant_share_with_work_apps_explanation))
- .check(matches(isDisplayed()));
- }
-
- private void assertCantSharePersonalAppsBlockerDisplayed() {
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- onView(withText(R.string.resolver_cant_share_with_personal_apps_explanation))
- .check(matches(isDisplayed()));
- }
-
- private void assertNoBlockerDisplayed() {
- try {
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(not(isDisplayed())));
- } catch (NoMatchingViewException ignored) {
- }
- }
-
- private void switchToTab(Tab tab) {
- final int stringId = tab == Tab.WORK ? R.string.resolver_work_tab
- : R.string.resolver_personal_tab;
-
- waitFor(() -> {
- onView(withText(stringId)).perform(click());
- waitForIdle();
-
- try {
- onView(withText(stringId)).check(matches(isSelected()));
- return true;
- } catch (AssertionFailedError e) {
- return false;
- }
- });
-
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- waitForIdle();
- }
-
- private Intent createTextIntent(boolean isSendAction) {
- Intent sendIntent = new Intent();
- if (isSendAction) {
- sendIntent.setAction(Intent.ACTION_SEND);
- }
- sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
- sendIntent.setType("text/plain");
- return sendIntent;
- }
-
- private void launchActivity(boolean isSendAction) {
- Intent sendIntent = createTextIntent(isSendAction);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "Test"));
- waitForIdle();
- }
-
- public static class TestCase {
- private final boolean mIsSendAction;
- private final boolean mHasCrossProfileIntents;
- private final UserHandle mMyUserHandle;
- private final Tab mTab;
- private final ExpectedBlocker mExpectedBlocker;
-
- public enum ExpectedBlocker {
- NO_BLOCKER,
- PERSONAL_PROFILE_SHARE_BLOCKER,
- WORK_PROFILE_SHARE_BLOCKER,
- PERSONAL_PROFILE_ACCESS_BLOCKER,
- WORK_PROFILE_ACCESS_BLOCKER
- }
-
- public enum Tab {
- WORK,
- PERSONAL
- }
-
- public TestCase(boolean isSendAction, boolean hasCrossProfileIntents,
- UserHandle myUserHandle, Tab tab, ExpectedBlocker expectedBlocker) {
- mIsSendAction = isSendAction;
- mHasCrossProfileIntents = hasCrossProfileIntents;
- mMyUserHandle = myUserHandle;
- mTab = tab;
- mExpectedBlocker = expectedBlocker;
- }
-
- public boolean getIsSendAction() {
- return mIsSendAction;
- }
-
- public boolean hasCrossProfileIntents() {
- return mHasCrossProfileIntents;
- }
-
- public UserHandle getMyUserHandle() {
- return mMyUserHandle;
- }
-
- public Tab getTab() {
- return mTab;
- }
-
- public ExpectedBlocker getExpectedBlocker() {
- return mExpectedBlocker;
- }
-
- @Override
- public String toString() {
- StringBuilder result = new StringBuilder("test");
-
- if (mTab == WORK) {
- result.append("WorkTab_");
- } else {
- result.append("PersonalTab_");
- }
-
- if (mIsSendAction) {
- result.append("sendAction_");
- } else {
- result.append("notSendAction_");
- }
-
- if (mHasCrossProfileIntents) {
- result.append("hasCrossProfileIntents_");
- } else {
- result.append("doesNotHaveCrossProfileIntents_");
- }
-
- if (mMyUserHandle.equals(PERSONAL_USER_HANDLE)) {
- result.append("myUserIsPersonal_");
- } else {
- result.append("myUserIsWork_");
- }
-
- if (mExpectedBlocker == ExpectedBlocker.NO_BLOCKER) {
- result.append("thenNoBlocker");
- } else if (mExpectedBlocker == PERSONAL_PROFILE_ACCESS_BLOCKER) {
- result.append("thenAccessBlockerOnPersonalProfile");
- } else if (mExpectedBlocker == PERSONAL_PROFILE_SHARE_BLOCKER) {
- result.append("thenShareBlockerOnPersonalProfile");
- } else if (mExpectedBlocker == WORK_PROFILE_ACCESS_BLOCKER) {
- result.append("thenAccessBlockerOnWorkProfile");
- } else if (mExpectedBlocker == WORK_PROFILE_SHARE_BLOCKER) {
- result.append("thenShareBlockerOnWorkProfile");
- }
-
- return result.toString();
- }
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt b/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt
deleted file mode 100644
index 6712bf31..00000000
--- a/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *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.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
-import androidx.test.platform.app.InstrumentationRegistry
-
-class ImmutableTargetInfoTest {
- private val PERSONAL_USER_HANDLE: UserHandle = InstrumentationRegistry
- .getInstrumentation().getTargetContext().getUser()
-
- 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, PERSONAL_USER_HANDLE)
- 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, PERSONAL_USER_HANDLE),
- "display1 label",
- "display1 extended info",
- Intent("display1_resolved")
- )
- private val displayTarget2 = DisplayResolveInfo.newDisplayResolveInfo(
- Intent("display2"),
- ResolverDataProvider.createResolveInfo(3, 0, PERSONAL_USER_HANDLE),
- "display2 label",
- "display2 extended info",
- Intent("display2_resolved")
- )
- 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, PERSONAL_USER_HANDLE),
- "displayresolve label",
- "displayresolve extended info",
- Intent("display_resolved")
- )
- 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 = checkNotNull(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 =
- checkNotNull(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 = checkNotNull(originalInfo.tryToCloneWithAppliedRefinement(refinementIntent1))
- // Cloned clone.
- val refined2 = checkNotNull(refined1.tryToCloneWithAppliedRefinement(refinementIntent2))
-
- // 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 = checkNotNull(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
deleted file mode 100644
index a7574c12..00000000
--- a/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * 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.chooser
-
-import android.app.prediction.AppTarget
-import android.app.prediction.AppTargetId
-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.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 PERSONAL_USER_HANDLE: UserHandle = InstrumentationRegistry
- .getInstrumentation().getTargetContext().getUser()
-
- 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()
- assertThat(info.isEmptyTargetInfo()).isTrue()
- assertThat(info.isChooserTargetInfo()).isTrue() // From legacy inheritance model.
- assertThat(info.hasDisplayIcon()).isFalse()
- 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.hasDisplayIcon()).isTrue()
- 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
- fun testNewSelectableTargetInfo() {
- val resolvedIntent = Intent()
- val baseDisplayInfo = DisplayResolveInfo.newDisplayResolveInfo(
- resolvedIntent,
- ResolverDataProvider.createResolveInfo(1, 0, PERSONAL_USER_HANDLE),
- "label",
- "extended info",
- resolvedIntent
- )
- 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(),
- )
- assertThat(targetInfo.isSelectableTargetInfo).isTrue()
- assertThat(targetInfo.isChooserTargetInfo).isTrue() // From legacy inheritance model.
- assertThat(targetInfo.displayResolveInfo).isSameInstanceAs(baseDisplayInfo)
- assertThat(targetInfo.chooserTargetComponentName).isEqualTo(chooserTarget.componentName)
- assertThat(targetInfo.directShareShortcutId).isEqualTo(shortcutInfo.id)
- assertThat(targetInfo.directShareShortcutInfo).isSameInstanceAs(shortcutInfo)
- assertThat(targetInfo.directShareAppTarget).isSameInstanceAs(appTarget)
- assertThat(targetInfo.resolvedIntent).isSameInstanceAs(resolvedIntent)
- // TODO: make more meaningful assertions about the behavior of a selectable target.
- }
-
- @Test
- fun test_SelectableTargetInfo_componentName_no_source_info() {
- val chooserTarget = createChooserTarget(
- "title", 0.3f, ResolverDataProvider.createComponentName(1), "test_shortcut_id")
- val shortcutInfo = createShortcutInfo("id", ResolverDataProvider.createComponentName(2), 3)
- val appTarget = AppTarget(
- AppTargetId("id"),
- chooserTarget.componentName.packageName,
- chooserTarget.componentName.className,
- UserHandle.CURRENT)
- val pkgName = "org.package"
- val className = "MainActivity"
- val backupResolveInfo = ResolveInfo().apply {
- activityInfo = ActivityInfo().apply {
- packageName = pkgName
- name = className
- }
- }
-
- val targetInfo = SelectableTargetInfo.newSelectableTargetInfo(
- null,
- backupResolveInfo,
- mock(),
- chooserTarget,
- 0.1f,
- shortcutInfo,
- appTarget,
- mock(),
- )
- assertThat(targetInfo.resolvedComponentName).isEqualTo(ComponentName(pkgName, className))
- }
-
- @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
- )
- 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")
- intent.setType("text/plain")
-
- val resolveInfo = ResolverDataProvider.createResolveInfo(3, 0, PERSONAL_USER_HANDLE)
-
- val targetInfo = DisplayResolveInfo.newDisplayResolveInfo(
- intent,
- resolveInfo,
- "label",
- "extended info",
- intent
- )
- assertThat(targetInfo.isDisplayResolveInfo()).isTrue()
- assertThat(targetInfo.isMultiDisplayResolveInfo()).isFalse()
- assertThat(targetInfo.isChooserTargetInfo()).isFalse()
- }
-
- @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
- )
- originalInfo.addAlternateSourceIntent(mismatchedAlternate)
- originalInfo.addAlternateSourceIntent(targetAlternate)
- originalInfo.addAlternateSourceIntent(extraMatch)
-
- val refinement = Intent("REFINE_ME") // First match is `targetAlternate`
- refinement.putExtra("refinement", true)
-
- val refinedResult = checkNotNull(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
- )
- 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")
- intent.setType("text/plain")
-
- val resolveInfo = ResolverDataProvider.createResolveInfo(3, 0, PERSONAL_USER_HANDLE)
- val firstTargetInfo = DisplayResolveInfo.newDisplayResolveInfo(
- intent,
- resolveInfo,
- "label 1",
- "extended info 1",
- intent
- )
- val secondTargetInfo = DisplayResolveInfo.newDisplayResolveInfo(
- intent,
- resolveInfo,
- "label 2",
- "extended info 2",
- intent
- )
-
- val multiTargetInfo = MultiDisplayResolveInfo.newMultiDisplayResolveInfo(
- listOf(firstTargetInfo, secondTargetInfo))
-
- assertThat(multiTargetInfo.isMultiDisplayResolveInfo()).isTrue()
- assertThat(multiTargetInfo.isDisplayResolveInfo()).isTrue() // From legacy inheritance.
- assertThat(multiTargetInfo.isChooserTargetInfo()).isFalse()
-
- assertThat(multiTargetInfo.getExtendedInfo()).isNull()
-
- assertThat(multiTargetInfo.getAllDisplayTargets())
- .containsExactly(firstTargetInfo, secondTargetInfo)
-
- assertThat(multiTargetInfo.hasSelected()).isFalse()
- assertThat(multiTargetInfo.getSelectedTarget()).isNull()
-
- multiTargetInfo.setSelected(1)
-
- 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
- )
-
- val textOnlyTarget = DisplayResolveInfo.newDisplayResolveInfo(
- sendUri,
- resolveInfo,
- "Send Text",
- "Sends only text",
- sendUri
- )
-
- val imageOrTextTarget = DisplayResolveInfo.newDisplayResolveInfo(
- sendImage,
- resolveInfo,
- "Send Image or Text",
- "Sends images or text",
- sendImage
- ).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
- )
- )
- 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
deleted file mode 100644
index 55cde497..00000000
--- a/java/tests/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.contentpreview
-
-import android.content.Intent
-import android.net.Uri
-import com.android.intentresolver.TestPreviewImageLoader
-import com.android.intentresolver.contentpreview.ChooserContentPreviewUi.ActionFactory
-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 java.util.function.Consumer
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-
-class ChooserContentPreviewUiTest {
- private val testScope = TestScope(EmptyCoroutineContext + UnconfinedTestDispatcher())
- private val previewData = mock<PreviewDataProvider>()
- private val headlineGenerator = mock<HeadlineGenerator>()
- private val imageLoader = TestPreviewImageLoader(emptyMap())
- private val actionFactory =
- object : ActionFactory {
- override fun getCopyButtonRunnable(): Runnable? = null
- override fun getEditButtonRunnable(): Runnable? = null
- override fun createCustomActions(): List<ActionRow.Action> = emptyList()
- override fun getModifyShareAction(): ActionRow.Action? = null
- override fun getExcludeSharedTextAction(): Consumer<Boolean> = Consumer<Boolean> {}
- }
- private val transitionCallback = mock<ImagePreviewView.TransitionElementStatusCallback>()
-
- @Test
- fun test_textPreviewType_useTextPreviewUi() {
- whenever(previewData.previewType).thenReturn(ContentPreviewType.CONTENT_PREVIEW_TEXT)
- val testSubject =
- ChooserContentPreviewUi(
- testScope,
- previewData,
- Intent(Intent.ACTION_VIEW),
- imageLoader,
- actionFactory,
- transitionCallback,
- headlineGenerator,
- )
- assertThat(testSubject.preferredContentPreview)
- .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT)
- assertThat(testSubject.mContentPreviewUi).isInstanceOf(TextContentPreviewUi::class.java)
- verify(transitionCallback, times(1)).onAllTransitionElementsReady()
- }
-
- @Test
- fun test_filePreviewType_useFilePreviewUi() {
- whenever(previewData.previewType).thenReturn(ContentPreviewType.CONTENT_PREVIEW_FILE)
- val testSubject =
- ChooserContentPreviewUi(
- testScope,
- previewData,
- Intent(Intent.ACTION_SEND),
- imageLoader,
- actionFactory,
- transitionCallback,
- headlineGenerator,
- )
- assertThat(testSubject.preferredContentPreview)
- .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- assertThat(testSubject.mContentPreviewUi).isInstanceOf(FileContentPreviewUi::class.java)
- verify(transitionCallback, times(1)).onAllTransitionElementsReady()
- }
-
- @Test
- fun test_imagePreviewTypeWithText_useFilePlusTextPreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/img.png")
- whenever(previewData.previewType).thenReturn(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
- whenever(previewData.uriCount).thenReturn(2)
- whenever(previewData.firstFileInfo)
- .thenReturn(FileInfo.Builder(uri).withPreviewUri(uri).withMimeType("image/png").build())
- whenever(previewData.imagePreviewFileInfoFlow).thenReturn(MutableSharedFlow())
- val testSubject =
- ChooserContentPreviewUi(
- testScope,
- previewData,
- Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_TEXT, "Shared text") },
- imageLoader,
- actionFactory,
- transitionCallback,
- headlineGenerator,
- )
- assertThat(testSubject.mContentPreviewUi)
- .isInstanceOf(FilesPlusTextContentPreviewUi::class.java)
- verify(previewData, times(1)).imagePreviewFileInfoFlow
- verify(transitionCallback, times(1)).onAllTransitionElementsReady()
- }
-
- @Test
- fun test_imagePreviewTypeWithoutText_useImagePreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/img.png")
- whenever(previewData.previewType).thenReturn(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
- whenever(previewData.uriCount).thenReturn(2)
- whenever(previewData.firstFileInfo)
- .thenReturn(FileInfo.Builder(uri).withPreviewUri(uri).withMimeType("image/png").build())
- whenever(previewData.imagePreviewFileInfoFlow).thenReturn(MutableSharedFlow())
- val testSubject =
- ChooserContentPreviewUi(
- testScope,
- previewData,
- Intent(Intent.ACTION_SEND),
- imageLoader,
- actionFactory,
- transitionCallback,
- headlineGenerator,
- )
- assertThat(testSubject.preferredContentPreview)
- .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
- assertThat(testSubject.mContentPreviewUi).isInstanceOf(UnifiedContentPreviewUi::class.java)
- verify(previewData, times(1)).imagePreviewFileInfoFlow
- verify(transitionCallback, never()).onAllTransitionElementsReady()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/contentpreview/ContentPreviewUiTest.kt b/java/tests/src/com/android/intentresolver/contentpreview/ContentPreviewUiTest.kt
deleted file mode 100644
index 6db53a9e..00000000
--- a/java/tests/src/com/android/intentresolver/contentpreview/ContentPreviewUiTest.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.contentpreview
-
-import com.android.intentresolver.widget.ScrollableImagePreviewView.PreviewType
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-
-class ContentPreviewUiTest {
- @Test
- fun testPreviewTypes() {
- val typeClassifier =
- object : MimeTypeClassifier {
- override fun isImageType(type: String?) = (type == "image")
- override fun isVideoType(type: String?) = (type == "video")
- }
-
- assertThat(ContentPreviewUi.getPreviewType(typeClassifier, "image"))
- .isEqualTo(PreviewType.Image)
- assertThat(ContentPreviewUi.getPreviewType(typeClassifier, "video"))
- .isEqualTo(PreviewType.Video)
- assertThat(ContentPreviewUi.getPreviewType(typeClassifier, "other"))
- .isEqualTo(PreviewType.File)
- assertThat(ContentPreviewUi.getPreviewType(typeClassifier, null))
- .isEqualTo(PreviewType.File)
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/contentpreview/FileContentPreviewUiTest.kt b/java/tests/src/com/android/intentresolver/contentpreview/FileContentPreviewUiTest.kt
deleted file mode 100644
index d2d952ae..00000000
--- a/java/tests/src/com/android/intentresolver/contentpreview/FileContentPreviewUiTest.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.contentpreview
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.intentresolver.R
-import com.android.intentresolver.mock
-import com.android.intentresolver.whenever
-import com.android.intentresolver.widget.ActionRow
-import com.google.common.truth.Truth.assertThat
-import java.util.function.Consumer
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class FileContentPreviewUiTest {
- private val fileCount = 2
- private val text = "Sharing 2 files"
- private val actionFactory =
- object : ChooserContentPreviewUi.ActionFactory {
- override fun getEditButtonRunnable(): Runnable? = null
- override fun getCopyButtonRunnable(): Runnable? = null
- override fun createCustomActions(): List<ActionRow.Action> = emptyList()
- override fun getModifyShareAction(): ActionRow.Action? = null
- override fun getExcludeSharedTextAction(): Consumer<Boolean> = Consumer<Boolean> {}
- }
- private val headlineGenerator =
- mock<HeadlineGenerator> { whenever(getFilesHeadline(fileCount)).thenReturn(text) }
-
- private val context
- get() = InstrumentationRegistry.getInstrumentation().context
-
- private val testSubject =
- FileContentPreviewUi(
- fileCount,
- actionFactory,
- headlineGenerator,
- )
-
- @Test
- fun test_display_titleIsDisplayed() {
- val layoutInflater = LayoutInflater.from(context)
- val gridLayout = layoutInflater.inflate(R.layout.chooser_grid, null, false) as ViewGroup
-
- val previewView =
- testSubject.display(
- context.resources,
- layoutInflater,
- gridLayout,
- /*headlineViewParent=*/ null
- )
-
- assertThat(previewView).isNotNull()
- val headlineView = previewView?.findViewById<TextView>(R.id.headline)
- assertThat(headlineView).isNotNull()
- assertThat(headlineView?.text).isEqualTo(text)
- }
-
- @Test
- fun test_displayWithExternalHeaderView() {
- val layoutInflater = LayoutInflater.from(context)
- val gridLayout =
- layoutInflater.inflate(R.layout.chooser_grid_scrollable_preview, null, false)
- as ViewGroup
- val externalHeaderView =
- gridLayout.requireViewById<View>(R.id.chooser_headline_row_container)
-
- assertThat(externalHeaderView.findViewById<View>(R.id.headline)).isNull()
-
- val previewView =
- testSubject.display(context.resources, layoutInflater, gridLayout, externalHeaderView)
-
- assertThat(previewView).isNotNull()
- assertThat(previewView.findViewById<View>(R.id.headline)).isNull()
-
- val headlineView = externalHeaderView.findViewById<TextView>(R.id.headline)
- assertThat(headlineView).isNotNull()
- assertThat(headlineView?.text).isEqualTo(text)
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUiTest.kt b/java/tests/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUiTest.kt
deleted file mode 100644
index 7cc0b4b2..00000000
--- a/java/tests/src/com/android/intentresolver/contentpreview/FilesPlusTextContentPreviewUiTest.kt
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.contentpreview
-
-import android.net.Uri
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
-import com.android.intentresolver.R
-import com.android.intentresolver.mock
-import com.android.intentresolver.whenever
-import com.android.intentresolver.widget.ActionRow
-import com.google.common.truth.Truth.assertThat
-import com.google.common.truth.Truth.assertWithMessage
-import java.util.function.Consumer
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-
-private const val HEADLINE_IMAGES = "Image Headline"
-private const val HEADLINE_VIDEOS = "Video Headline"
-private const val HEADLINE_FILES = "Files Headline"
-private const val SHARED_TEXT = "Some text to share"
-
-@RunWith(AndroidJUnit4::class)
-class FilesPlusTextContentPreviewUiTest {
- private val testScope = TestScope(EmptyCoroutineContext + UnconfinedTestDispatcher())
- private val actionFactory =
- object : ChooserContentPreviewUi.ActionFactory {
- override fun getEditButtonRunnable(): Runnable? = null
- override fun getCopyButtonRunnable(): Runnable? = null
- override fun createCustomActions(): List<ActionRow.Action> = emptyList()
- override fun getModifyShareAction(): ActionRow.Action? = null
- override fun getExcludeSharedTextAction(): Consumer<Boolean> = Consumer<Boolean> {}
- }
- private val imageLoader = mock<ImageLoader>()
- private val headlineGenerator =
- mock<HeadlineGenerator> {
- whenever(getImagesHeadline(anyInt())).thenReturn(HEADLINE_IMAGES)
- whenever(getVideosHeadline(anyInt())).thenReturn(HEADLINE_VIDEOS)
- whenever(getFilesHeadline(anyInt())).thenReturn(HEADLINE_FILES)
- }
-
- private val context
- get() = getInstrumentation().context
-
- @Test
- fun test_displayImagesPlusTextWithoutUriMetadata_showImagesHeadline() {
- val sharedFileCount = 2
- val previewView = testLoadingHeadline("image/*", sharedFileCount)
-
- verify(headlineGenerator, times(1)).getImagesHeadline(sharedFileCount)
- verifyPreviewHeadline(previewView, HEADLINE_IMAGES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayImagesPlusTextWithoutUriMetadataExternalHeader_showImagesHeadline() {
- val sharedFileCount = 2
- val (previewView, headerParent) = testLoadingExternalHeadline("image/*", sharedFileCount)
-
- verify(headlineGenerator, times(1)).getImagesHeadline(sharedFileCount)
- verifyInternalHeadlineAbsence(previewView)
- verifyPreviewHeadline(headerParent, HEADLINE_IMAGES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayVideosPlusTextWithoutUriMetadata_showVideosHeadline() {
- val sharedFileCount = 2
- val previewView = testLoadingHeadline("video/*", sharedFileCount)
-
- verify(headlineGenerator, times(1)).getVideosHeadline(sharedFileCount)
- verifyPreviewHeadline(previewView, HEADLINE_VIDEOS)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayVideosPlusTextWithoutUriMetadataExternalHeader_showVideosHeadline() {
- val sharedFileCount = 2
- val (previewView, headerParent) = testLoadingExternalHeadline("video/*", sharedFileCount)
-
- verify(headlineGenerator, times(1)).getVideosHeadline(sharedFileCount)
- verifyInternalHeadlineAbsence(previewView)
- verifyPreviewHeadline(headerParent, HEADLINE_VIDEOS)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayDocsPlusTextWithoutUriMetadata_showFilesHeadline() {
- val sharedFileCount = 2
- val previewView = testLoadingHeadline("application/pdf", sharedFileCount)
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verifyPreviewHeadline(previewView, HEADLINE_FILES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayDocsPlusTextWithoutUriMetadataExternalHeader_showFilesHeadline() {
- val sharedFileCount = 2
- val (previewView, headerParent) =
- testLoadingExternalHeadline("application/pdf", sharedFileCount)
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verifyInternalHeadlineAbsence(previewView)
- verifyPreviewHeadline(headerParent, HEADLINE_FILES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayMixedContentPlusTextWithoutUriMetadata_showFilesHeadline() {
- val sharedFileCount = 2
- val previewView = testLoadingHeadline("*/*", sharedFileCount)
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verifyPreviewHeadline(previewView, HEADLINE_FILES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayMixedContentPlusTextWithoutUriMetadataExternalHeader_showFilesHeadline() {
- val sharedFileCount = 2
- val (previewView, headerParent) = testLoadingExternalHeadline("*/*", sharedFileCount)
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verifyInternalHeadlineAbsence(previewView)
- verifyPreviewHeadline(headerParent, HEADLINE_FILES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayImagesPlusTextWithUriMetadataSet_showImagesHeadline() {
- val loadedFileMetadata = createFileInfosWithMimeTypes("image/png", "image/jpeg")
- val sharedFileCount = loadedFileMetadata.size
- val previewView = testLoadingHeadline("image/*", sharedFileCount, loadedFileMetadata)
-
- verify(headlineGenerator, times(1)).getImagesHeadline(sharedFileCount)
- verifyPreviewHeadline(previewView, HEADLINE_IMAGES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayImagesPlusTextWithUriMetadataSetExternalHeader_showImagesHeadline() {
- val loadedFileMetadata = createFileInfosWithMimeTypes("image/png", "image/jpeg")
- val sharedFileCount = loadedFileMetadata.size
- val (previewView, headerParent) =
- testLoadingExternalHeadline("image/*", sharedFileCount, loadedFileMetadata)
-
- verify(headlineGenerator, times(1)).getImagesHeadline(sharedFileCount)
- verifyInternalHeadlineAbsence(previewView)
- verifyPreviewHeadline(headerParent, HEADLINE_IMAGES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayVideosPlusTextWithUriMetadataSet_showVideosHeadline() {
- val loadedFileMetadata = createFileInfosWithMimeTypes("video/mp4", "video/mp4")
- val sharedFileCount = loadedFileMetadata.size
- val previewView = testLoadingHeadline("video/*", sharedFileCount, loadedFileMetadata)
-
- verify(headlineGenerator, times(1)).getVideosHeadline(sharedFileCount)
- verifyPreviewHeadline(previewView, HEADLINE_VIDEOS)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayVideosPlusTextWithUriMetadataSetExternalHeader_showVideosHeadline() {
- val loadedFileMetadata = createFileInfosWithMimeTypes("video/mp4", "video/mp4")
- val sharedFileCount = loadedFileMetadata.size
- val (previewView, headerParent) =
- testLoadingExternalHeadline("video/*", sharedFileCount, loadedFileMetadata)
-
- verify(headlineGenerator, times(1)).getVideosHeadline(sharedFileCount)
- verifyInternalHeadlineAbsence(previewView)
- verifyPreviewHeadline(headerParent, HEADLINE_VIDEOS)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayImagesAndVideosPlusTextWithUriMetadataSet_showFilesHeadline() {
- val loadedFileMetadata = createFileInfosWithMimeTypes("image/png", "video/mp4")
- val sharedFileCount = loadedFileMetadata.size
- val previewView = testLoadingHeadline("*/*", sharedFileCount, loadedFileMetadata)
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verifyPreviewHeadline(previewView, HEADLINE_FILES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayImagesAndVideosPlusTextWithUriMetadataSetExternalHeader_showFilesHeadline() {
- val loadedFileMetadata = createFileInfosWithMimeTypes("image/png", "video/mp4")
- val sharedFileCount = loadedFileMetadata.size
- val (previewView, headerParent) =
- testLoadingExternalHeadline("*/*", sharedFileCount, loadedFileMetadata)
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verifyInternalHeadlineAbsence(previewView)
- verifyPreviewHeadline(headerParent, HEADLINE_FILES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayDocsPlusTextWithUriMetadataSet_showFilesHeadline() {
- val loadedFileMetadata = createFileInfosWithMimeTypes("application/pdf", "application/pdf")
- val sharedFileCount = loadedFileMetadata.size
- val previewView =
- testLoadingHeadline("application/pdf", sharedFileCount, loadedFileMetadata)
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verifyPreviewHeadline(previewView, HEADLINE_FILES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_displayDocsPlusTextWithUriMetadataSetExternalHeader_showFilesHeadline() {
- val loadedFileMetadata = createFileInfosWithMimeTypes("application/pdf", "application/pdf")
- val sharedFileCount = loadedFileMetadata.size
- val (previewView, headerParent) =
- testLoadingExternalHeadline("application/pdf", sharedFileCount, loadedFileMetadata)
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verifyInternalHeadlineAbsence(previewView)
- verifyPreviewHeadline(headerParent, HEADLINE_FILES)
- verifySharedText(previewView)
- }
-
- @Test
- fun test_uriMetadataIsMoreSpecificThanIntentMimeType_headlineGetsUpdated() {
- val sharedFileCount = 2
- val testSubject =
- FilesPlusTextContentPreviewUi(
- testScope,
- /*isSingleImage=*/ false,
- sharedFileCount,
- SHARED_TEXT,
- /*intentMimeType=*/ "*/*",
- actionFactory,
- imageLoader,
- DefaultMimeTypeClassifier,
- headlineGenerator
- )
- val layoutInflater = LayoutInflater.from(context)
- val gridLayout = layoutInflater.inflate(R.layout.chooser_grid, null, false) as ViewGroup
-
- val previewView =
- testSubject.display(context.resources, LayoutInflater.from(context), gridLayout, null)
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verify(headlineGenerator, never()).getImagesHeadline(sharedFileCount)
- verifyPreviewHeadline(previewView, HEADLINE_FILES)
-
- testSubject.updatePreviewMetadata(createFileInfosWithMimeTypes("image/png", "image/jpg"))
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verify(headlineGenerator, times(1)).getImagesHeadline(sharedFileCount)
- verifyPreviewHeadline(previewView, HEADLINE_IMAGES)
- }
-
- @Test
- fun test_uriMetadataIsMoreSpecificThanIntentMimeTypeExternalHeader_headlineGetsUpdated() {
- val sharedFileCount = 2
- val testSubject =
- FilesPlusTextContentPreviewUi(
- testScope,
- /*isSingleImage=*/ false,
- sharedFileCount,
- SHARED_TEXT,
- /*intentMimeType=*/ "*/*",
- actionFactory,
- imageLoader,
- DefaultMimeTypeClassifier,
- headlineGenerator
- )
- val layoutInflater = LayoutInflater.from(context)
- val gridLayout =
- layoutInflater.inflate(R.layout.chooser_grid_scrollable_preview, null, false)
- as ViewGroup
- val externalHeaderView =
- gridLayout.requireViewById<View>(R.id.chooser_headline_row_container)
-
- assertWithMessage("External headline should not be inflated by default")
- .that(externalHeaderView.findViewById<View>(R.id.headline))
- .isNull()
-
- val previewView =
- testSubject.display(
- context.resources,
- LayoutInflater.from(context),
- gridLayout,
- externalHeaderView
- )
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verify(headlineGenerator, never()).getImagesHeadline(sharedFileCount)
- verifyInternalHeadlineAbsence(previewView)
- verifyPreviewHeadline(externalHeaderView, HEADLINE_FILES)
-
- testSubject.updatePreviewMetadata(createFileInfosWithMimeTypes("image/png", "image/jpg"))
-
- verify(headlineGenerator, times(1)).getFilesHeadline(sharedFileCount)
- verify(headlineGenerator, times(1)).getImagesHeadline(sharedFileCount)
- verifyPreviewHeadline(externalHeaderView, HEADLINE_IMAGES)
- }
-
- private fun testLoadingHeadline(
- intentMimeType: String,
- sharedFileCount: Int,
- loadedFileMetadata: List<FileInfo>? = null,
- ): ViewGroup? {
- val testSubject =
- FilesPlusTextContentPreviewUi(
- testScope,
- /*isSingleImage=*/ false,
- sharedFileCount,
- SHARED_TEXT,
- intentMimeType,
- actionFactory,
- imageLoader,
- DefaultMimeTypeClassifier,
- headlineGenerator
- )
- val layoutInflater = LayoutInflater.from(context)
- val gridLayout = layoutInflater.inflate(R.layout.chooser_grid, null, false) as ViewGroup
-
- loadedFileMetadata?.let(testSubject::updatePreviewMetadata)
- return testSubject.display(
- context.resources,
- LayoutInflater.from(context),
- gridLayout,
- /*headlineViewParent=*/ null
- )
- }
-
- private fun testLoadingExternalHeadline(
- intentMimeType: String,
- sharedFileCount: Int,
- loadedFileMetadata: List<FileInfo>? = null,
- ): Pair<ViewGroup?, View> {
- val testSubject =
- FilesPlusTextContentPreviewUi(
- testScope,
- /*isSingleImage=*/ false,
- sharedFileCount,
- SHARED_TEXT,
- intentMimeType,
- actionFactory,
- imageLoader,
- DefaultMimeTypeClassifier,
- headlineGenerator
- )
- val layoutInflater = LayoutInflater.from(context)
- val gridLayout =
- layoutInflater.inflate(R.layout.chooser_grid_scrollable_preview, null, false)
- as ViewGroup
- val externalHeaderView =
- gridLayout.requireViewById<View>(R.id.chooser_headline_row_container)
-
- assertWithMessage("External headline should not be inflated by default")
- .that(externalHeaderView.findViewById<View>(R.id.headline))
- .isNull()
-
- loadedFileMetadata?.let(testSubject::updatePreviewMetadata)
- return testSubject.display(
- context.resources,
- LayoutInflater.from(context),
- gridLayout,
- externalHeaderView
- ) to externalHeaderView
- }
-
- private fun createFileInfosWithMimeTypes(vararg mimeTypes: String): List<FileInfo> {
- val uri = Uri.parse("content://pkg.app/file")
- return mimeTypes.map { mimeType -> FileInfo.Builder(uri).withMimeType(mimeType).build() }
- }
-
- private fun verifyPreviewHeadline(headerViewParent: View?, expectedText: String) {
- assertThat(headerViewParent).isNotNull()
- val headlineView = headerViewParent?.findViewById<TextView>(R.id.headline)
- assertThat(headlineView).isNotNull()
- assertThat(headlineView?.text).isEqualTo(expectedText)
- }
-
- private fun verifySharedText(previewView: ViewGroup?) {
- assertThat(previewView).isNotNull()
- val textContentView = previewView?.findViewById<TextView>(R.id.content_preview_text)
- assertThat(textContentView).isNotNull()
- assertThat(textContentView?.text).isEqualTo(SHARED_TEXT)
- }
-
- private fun verifyInternalHeadlineAbsence(previewView: ViewGroup?) {
- assertWithMessage("Preview parent should not be null").that(previewView).isNotNull()
- assertWithMessage(
- "Preview headline should not be inflated when an external headline is used"
- )
- .that(previewView?.findViewById<View>(R.id.headline))
- .isNull()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/contentpreview/HeadlineGeneratorImplTest.kt b/java/tests/src/com/android/intentresolver/contentpreview/HeadlineGeneratorImplTest.kt
deleted file mode 100644
index a65280e5..00000000
--- a/java/tests/src/com/android/intentresolver/contentpreview/HeadlineGeneratorImplTest.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.contentpreview
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Test
-import org.junit.runner.RunWith
-import com.google.common.truth.Truth.assertThat
-
-@RunWith(AndroidJUnit4::class)
-class HeadlineGeneratorImplTest {
- @Test
- fun testHeadlineGeneration() {
- val generator = HeadlineGeneratorImpl(
- InstrumentationRegistry.getInstrumentation().getTargetContext())
- val str = "Some string"
- val url = "http://www.google.com"
-
- assertThat(generator.getTextHeadline(str)).isEqualTo("Sharing text")
- assertThat(generator.getTextHeadline(url)).isEqualTo("Sharing link")
-
- assertThat(generator.getImagesWithTextHeadline(str, 1)).isEqualTo("Sharing image with text")
- assertThat(generator.getImagesWithTextHeadline(url, 1)).isEqualTo("Sharing image with link")
- assertThat(generator.getImagesWithTextHeadline(str, 5)).isEqualTo("Sharing 5 images with text")
- assertThat(generator.getImagesWithTextHeadline(url, 5)).isEqualTo("Sharing 5 images with link")
-
- assertThat(generator.getVideosWithTextHeadline(str, 1)).isEqualTo("Sharing video with text")
- assertThat(generator.getVideosWithTextHeadline(url, 1)).isEqualTo("Sharing video with link")
- assertThat(generator.getVideosWithTextHeadline(str, 5)).isEqualTo("Sharing 5 videos with text")
- assertThat(generator.getVideosWithTextHeadline(url, 5)).isEqualTo("Sharing 5 videos with link")
-
- assertThat(generator.getFilesWithTextHeadline(str, 1)).isEqualTo("Sharing file with text")
- assertThat(generator.getFilesWithTextHeadline(url, 1)).isEqualTo("Sharing file with link")
- assertThat(generator.getFilesWithTextHeadline(str, 5)).isEqualTo("Sharing 5 files with text")
- assertThat(generator.getFilesWithTextHeadline(url, 5)).isEqualTo("Sharing 5 files with link")
-
- assertThat(generator.getImagesHeadline(1)).isEqualTo("Sharing image")
- assertThat(generator.getImagesHeadline(4)).isEqualTo("Sharing 4 images")
-
- assertThat(generator.getVideosHeadline(1)).isEqualTo("Sharing video")
- assertThat(generator.getVideosHeadline(4)).isEqualTo("Sharing 4 videos")
-
- assertThat(generator.getFilesHeadline(1)).isEqualTo("Sharing 1 file")
- assertThat(generator.getFilesHeadline(4)).isEqualTo("Sharing 4 files")
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt b/java/tests/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt
deleted file mode 100644
index 89978707..00000000
--- a/java/tests/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.contentpreview
-
-import android.content.ContentResolver
-import android.graphics.Bitmap
-import android.net.Uri
-import android.util.Size
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.coroutineScope
-import androidx.lifecycle.testing.TestLifecycleOwner
-import com.android.intentresolver.any
-import com.android.intentresolver.anyOrNull
-import com.android.intentresolver.mock
-import com.android.intentresolver.whenever
-import com.google.common.truth.Truth.assertThat
-import java.util.ArrayDeque
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit.MILLISECONDS
-import java.util.concurrent.TimeUnit.SECONDS
-import java.util.concurrent.atomic.AtomicInteger
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineName
-import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Runnable
-import kotlinx.coroutines.async
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.plus
-import kotlinx.coroutines.sync.Semaphore
-import kotlinx.coroutines.test.StandardTestDispatcher
-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 kotlinx.coroutines.yield
-import org.junit.After
-import org.junit.Assert.assertTrue
-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 bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
- private val contentResolver =
- mock<ContentResolver> {
- whenever(loadThumbnail(any(), any(), anyOrNull())).thenReturn(bitmap)
- }
- private val lifecycleOwner = TestLifecycleOwner()
- private val dispatcher = UnconfinedTestDispatcher()
- private lateinit var testSubject: ImagePreviewImageLoader
-
- @Before
- fun setup() {
- Dispatchers.setMain(dispatcher)
- lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
- // create test subject after we've updated the lifecycle dispatcher
- testSubject =
- ImagePreviewImageLoader(
- lifecycleOwner.lifecycle.coroutineScope + dispatcher,
- imageSize.width,
- contentResolver,
- cacheSize = 1,
- )
- }
-
- @After
- fun cleanup() {
- lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
- Dispatchers.resetMain()
- }
-
- @Test
- fun prePopulate_cachesImagesUpToTheCacheSize() = 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 invoke_returnCachedImageWhenCalledTwice() = runTest {
- testSubject(uriOne)
- testSubject(uriOne)
-
- verify(contentResolver, times(1)).loadThumbnail(any(), any(), anyOrNull())
- }
-
- @Test
- fun invoke_whenInstructed_doesNotCache() = runTest {
- testSubject(uriOne, false)
- testSubject(uriOne, false)
-
- verify(contentResolver, times(2)).loadThumbnail(any(), any(), anyOrNull())
- }
-
- @Test
- fun invoke_overlappedRequests_Deduplicate() = runTest {
- val scheduler = TestCoroutineScheduler()
- val dispatcher = StandardTestDispatcher(scheduler)
- val testSubject =
- ImagePreviewImageLoader(
- lifecycleOwner.lifecycle.coroutineScope + dispatcher,
- imageSize.width,
- contentResolver,
- cacheSize = 1,
- )
- coroutineScope {
- launch(start = UNDISPATCHED) { testSubject(uriOne, false) }
- launch(start = UNDISPATCHED) { testSubject(uriOne, false) }
- scheduler.advanceUntilIdle()
- }
-
- verify(contentResolver, times(1)).loadThumbnail(any(), any(), anyOrNull())
- }
-
- @Test
- fun invoke_oldRecordsEvictedFromTheCache() = 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)
- }
-
- @Test
- fun invoke_doNotCacheNulls() = runTest {
- whenever(contentResolver.loadThumbnail(any(), any(), anyOrNull())).thenReturn(null)
- testSubject(uriOne)
- testSubject(uriOne)
-
- verify(contentResolver, times(2)).loadThumbnail(uriOne, imageSize, null)
- }
-
- @Test(expected = CancellationException::class)
- fun invoke_onClosedImageLoaderScope_throwsCancellationException() = runTest {
- lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
- testSubject(uriOne)
- }
-
- @Test(expected = CancellationException::class)
- fun invoke_imageLoaderScopeClosedMidflight_throwsCancellationException() = runTest {
- val scheduler = TestCoroutineScheduler()
- val dispatcher = StandardTestDispatcher(scheduler)
- val testSubject =
- ImagePreviewImageLoader(
- lifecycleOwner.lifecycle.coroutineScope + dispatcher,
- imageSize.width,
- contentResolver,
- cacheSize = 1,
- )
- coroutineScope {
- val deferred = async(start = UNDISPATCHED) { testSubject(uriOne, false) }
- lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
- scheduler.advanceUntilIdle()
- deferred.await()
- }
- }
-
- @Test
- fun invoke_multipleCallsWithDifferentCacheInstructions_cachingPrevails() = runTest {
- val scheduler = TestCoroutineScheduler()
- val dispatcher = StandardTestDispatcher(scheduler)
- val testSubject =
- ImagePreviewImageLoader(
- lifecycleOwner.lifecycle.coroutineScope + dispatcher,
- imageSize.width,
- contentResolver,
- cacheSize = 1,
- )
- coroutineScope {
- launch(start = UNDISPATCHED) { testSubject(uriOne, false) }
- launch(start = UNDISPATCHED) { testSubject(uriOne, true) }
- scheduler.advanceUntilIdle()
- }
- testSubject(uriOne, true)
-
- verify(contentResolver, times(1)).loadThumbnail(uriOne, imageSize, null)
- }
-
- @Test
- fun invoke_semaphoreGuardsContentResolverCalls() = runTest {
- val contentResolver =
- mock<ContentResolver> {
- whenever(loadThumbnail(any(), any(), anyOrNull()))
- .thenThrow(SecurityException("test"))
- }
- val acquireCount = AtomicInteger()
- val releaseCount = AtomicInteger()
- val testSemaphore =
- object : Semaphore {
- override val availablePermits: Int
- get() = error("Unexpected invocation")
-
- override suspend fun acquire() {
- acquireCount.getAndIncrement()
- }
-
- override fun tryAcquire(): Boolean {
- error("Unexpected invocation")
- }
-
- override fun release() {
- releaseCount.getAndIncrement()
- }
- }
-
- val testSubject =
- ImagePreviewImageLoader(
- lifecycleOwner.lifecycle.coroutineScope + dispatcher,
- imageSize.width,
- contentResolver,
- cacheSize = 1,
- testSemaphore,
- )
- testSubject(uriOne, false)
-
- verify(contentResolver, times(1)).loadThumbnail(uriOne, imageSize, null)
- assertThat(acquireCount.get()).isEqualTo(1)
- assertThat(releaseCount.get()).isEqualTo(1)
- }
-
- @Test
- fun invoke_semaphoreIsReleasedAfterContentResolverFailure() = runTest {
- val semaphoreDeferred = CompletableDeferred<Unit>()
- val releaseCount = AtomicInteger()
- val testSemaphore =
- object : Semaphore {
- override val availablePermits: Int
- get() = error("Unexpected invocation")
-
- override suspend fun acquire() {
- semaphoreDeferred.await()
- }
-
- override fun tryAcquire(): Boolean {
- error("Unexpected invocation")
- }
-
- override fun release() {
- releaseCount.getAndIncrement()
- }
- }
-
- val testSubject =
- ImagePreviewImageLoader(
- lifecycleOwner.lifecycle.coroutineScope + dispatcher,
- imageSize.width,
- contentResolver,
- cacheSize = 1,
- testSemaphore,
- )
- launch(start = UNDISPATCHED) { testSubject(uriOne, false) }
-
- verify(contentResolver, never()).loadThumbnail(any(), any(), anyOrNull())
-
- semaphoreDeferred.complete(Unit)
-
- verify(contentResolver, times(1)).loadThumbnail(uriOne, imageSize, null)
- assertThat(releaseCount.get()).isEqualTo(1)
- }
-
- @Test
- fun invoke_multipleSimultaneousCalls_limitOnNumberOfSimultaneousOutgoingCallsIsRespected() {
- val requestCount = 4
- val thumbnailCallsCdl = CountDownLatch(requestCount)
- val pendingThumbnailCalls = ArrayDeque<CountDownLatch>()
- val contentResolver =
- mock<ContentResolver> {
- whenever(loadThumbnail(any(), any(), anyOrNull())).thenAnswer {
- val latch = CountDownLatch(1)
- synchronized(pendingThumbnailCalls) { pendingThumbnailCalls.offer(latch) }
- thumbnailCallsCdl.countDown()
- assertTrue("Timeout waiting thumbnail calls", latch.await(1, SECONDS))
- bitmap
- }
- }
- val name = "LoadImage"
- val maxSimultaneousRequests = 2
- val threadsStartedCdl = CountDownLatch(requestCount)
- val dispatcher = NewThreadDispatcher(name) { threadsStartedCdl.countDown() }
- val testSubject =
- ImagePreviewImageLoader(
- lifecycleOwner.lifecycle.coroutineScope + dispatcher + CoroutineName(name),
- imageSize.width,
- contentResolver,
- cacheSize = 1,
- maxSimultaneousRequests,
- )
- runTest {
- repeat(requestCount) {
- launch { testSubject(Uri.parse("content://org.pkg.app/image-$it.png")) }
- }
- yield()
- // wait for all requests to be dispatched
- assertThat(threadsStartedCdl.await(5, SECONDS)).isTrue()
-
- assertThat(thumbnailCallsCdl.await(100, MILLISECONDS)).isFalse()
- synchronized(pendingThumbnailCalls) {
- assertThat(pendingThumbnailCalls.size).isEqualTo(maxSimultaneousRequests)
- }
-
- pendingThumbnailCalls.poll()?.countDown()
- assertThat(thumbnailCallsCdl.await(100, MILLISECONDS)).isFalse()
- synchronized(pendingThumbnailCalls) {
- assertThat(pendingThumbnailCalls.size).isEqualTo(maxSimultaneousRequests)
- }
-
- pendingThumbnailCalls.poll()?.countDown()
- assertThat(thumbnailCallsCdl.await(100, MILLISECONDS)).isTrue()
- synchronized(pendingThumbnailCalls) {
- assertThat(pendingThumbnailCalls.size).isEqualTo(maxSimultaneousRequests)
- }
- for (cdl in pendingThumbnailCalls) {
- cdl.countDown()
- }
- }
- }
-}
-
-private class NewThreadDispatcher(
- private val coroutineName: String,
- private val launchedCallback: () -> Unit
-) : CoroutineDispatcher() {
- override fun isDispatchNeeded(context: CoroutineContext): Boolean = true
-
- override fun dispatch(context: CoroutineContext, block: Runnable) {
- Thread {
- if (coroutineName == context[CoroutineName.Key]?.name) {
- launchedCallback()
- }
- block.run()
- }
- .start()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt b/java/tests/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt
deleted file mode 100644
index 4a8c1392..00000000
--- a/java/tests/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.contentpreview
-
-import android.content.ContentInterface
-import android.content.Intent
-import android.database.MatrixCursor
-import android.media.MediaMetadata
-import android.net.Uri
-import android.provider.DocumentsContract
-import com.android.intentresolver.mock
-import com.android.intentresolver.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.toList
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.mockito.Mockito.any
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class PreviewDataProviderTest {
- private val contentResolver = mock<ContentInterface>()
- private val mimeTypeClassifier = DefaultMimeTypeClassifier
- private val testScope = TestScope(EmptyCoroutineContext + UnconfinedTestDispatcher())
-
- @Test
- fun test_nonSendIntentAction_resolvesToTextPreviewUiSynchronously() {
- val targetIntent = Intent(Intent.ACTION_VIEW)
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT)
- verify(contentResolver, never()).getType(any())
- }
-
- @Test
- fun test_sendSingleTextFileWithoutPreview_resolvesToFilePreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/notes.txt")
- val targetIntent =
- Intent(Intent.ACTION_SEND).apply {
- putExtra(Intent.EXTRA_STREAM, uri)
- type = "text/plain"
- }
- whenever(contentResolver.getType(uri)).thenReturn("text/plain")
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- assertThat(testSubject.uriCount).isEqualTo(1)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
- verify(contentResolver, times(1)).getType(any())
- }
-
- @Test
- fun test_sendIntentWithoutUris_resolvesToTextPreviewUiSynchronously() {
- val targetIntent = Intent(Intent.ACTION_SEND).apply { type = "image/png" }
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT)
- verify(contentResolver, never()).getType(any())
- }
-
- @Test
- fun test_sendSingleImage_resolvesToImagePreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/image.png")
- val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
- whenever(contentResolver.getType(uri)).thenReturn("image/png")
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
- assertThat(testSubject.uriCount).isEqualTo(1)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
- assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri)
- verify(contentResolver, times(1)).getType(any())
- }
-
- @Test
- fun test_sendSingleNonImage_resolvesToFilePreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/paper.pdf")
- val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
- whenever(contentResolver.getType(uri)).thenReturn("application/pdf")
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- assertThat(testSubject.uriCount).isEqualTo(1)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
- assertThat(testSubject.firstFileInfo?.previewUri).isNull()
- verify(contentResolver, times(1)).getType(any())
- }
-
- @Test
- fun test_sendSingleImageWithFailingGetType_resolvesToFilePreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/image.png")
- val targetIntent =
- Intent(Intent.ACTION_SEND).apply {
- type = "image/png"
- putExtra(Intent.EXTRA_STREAM, uri)
- }
- whenever(contentResolver.getType(uri)).thenThrow(SecurityException("test failure"))
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- assertThat(testSubject.uriCount).isEqualTo(1)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
- assertThat(testSubject.firstFileInfo?.previewUri).isNull()
- verify(contentResolver, times(1)).getType(any())
- }
-
- @Test
- fun test_sendSingleImageWithFailingMetadata_resolvesToFilePreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/image.png")
- val targetIntent =
- Intent(Intent.ACTION_SEND).apply {
- type = "image/png"
- putExtra(Intent.EXTRA_STREAM, uri)
- }
- whenever(contentResolver.getStreamTypes(uri, "*/*"))
- .thenThrow(SecurityException("test failure"))
- whenever(contentResolver.query(uri, METADATA_COLUMNS, null, null))
- .thenThrow(SecurityException("test failure"))
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- assertThat(testSubject.uriCount).isEqualTo(1)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
- assertThat(testSubject.firstFileInfo?.previewUri).isNull()
- verify(contentResolver, times(1)).getType(any())
- }
-
- @Test
- fun test_SingleNonImageUriWithImageTypeInGetStreamTypes_useImagePreviewUi() {
- val uri = Uri.parse("content://org.pkg.app/paper.pdf")
- val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
- whenever(contentResolver.getStreamTypes(uri, "*/*"))
- .thenReturn(arrayOf("application/pdf", "image/png"))
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
- assertThat(testSubject.uriCount).isEqualTo(1)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
- assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri)
- verify(contentResolver, times(1)).getType(any())
- }
-
- @Test
- fun test_SingleNonImageUriWithThumbnailFlag_useImagePreviewUi() {
- testMetadataToImagePreview(
- columns = arrayOf(DocumentsContract.Document.COLUMN_FLAGS),
- values =
- arrayOf(
- DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL or
- DocumentsContract.Document.FLAG_SUPPORTS_METADATA
- )
- )
- }
-
- @Test
- fun test_SingleNonImageUriWithMetadataIconUri_useImagePreviewUi() {
- testMetadataToImagePreview(
- columns = arrayOf(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI),
- values = arrayOf("content://org.pkg.app/test.pdf?thumbnail"),
- )
- }
-
- private fun testMetadataToImagePreview(columns: Array<String>, values: Array<Any>) {
- val uri = Uri.parse("content://org.pkg.app/test.pdf")
- val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
- whenever(contentResolver.getType(uri)).thenReturn("application/pdf")
- val cursor = MatrixCursor(columns).apply { addRow(values) }
- whenever(contentResolver.query(uri, METADATA_COLUMNS, null, null)).thenReturn(cursor)
-
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
- assertThat(testSubject.uriCount).isEqualTo(1)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
- assertThat(testSubject.firstFileInfo?.previewUri).isNotNull()
- verify(contentResolver, times(1)).getType(any())
- assertThat(cursor.isClosed).isTrue()
- }
-
- @Test
- fun test_emptyQueryResult_cursorGetsClosed() {
- val uri = Uri.parse("content://org.pkg.app/test.pdf")
- val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
- whenever(contentResolver.getType(uri)).thenReturn("application/pdf")
- val cursor = MatrixCursor(emptyArray())
- whenever(contentResolver.query(uri, METADATA_COLUMNS, null, null)).thenReturn(cursor)
-
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- verify(contentResolver, times(1)).query(uri, METADATA_COLUMNS, null, null)
- assertThat(cursor.isClosed).isTrue()
- }
-
- @Test
- fun test_multipleImageUri_useImagePreviewUi() {
- val uri1 = Uri.parse("content://org.pkg.app/test.png")
- val uri2 = Uri.parse("content://org.pkg.app/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 =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
- assertThat(testSubject.uriCount).isEqualTo(2)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri1)
- assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri1)
- // preview type can be determined by the first URI type
- verify(contentResolver, times(1)).getType(any())
- }
-
- @Test
- fun test_SomeImageUri_useImagePreviewUi() {
- val uri1 = Uri.parse("content://org.pkg.app/test.png")
- val uri2 = Uri.parse("content://org.pkg.app/test.pdf")
- whenever(contentResolver.getType(uri1)).thenReturn("image/png")
- whenever(contentResolver.getType(uri2)).thenReturn("application/pdf")
- val targetIntent =
- Intent(Intent.ACTION_SEND_MULTIPLE).apply {
- putExtra(
- Intent.EXTRA_STREAM,
- ArrayList<Uri>().apply {
- add(uri1)
- add(uri2)
- }
- )
- }
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
- assertThat(testSubject.uriCount).isEqualTo(2)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri1)
- assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri1)
- // preview type can be determined by the first URI type
- verify(contentResolver, times(1)).getType(any())
- }
-
- @Test
- fun test_someNonImageUriWithPreview_useImagePreviewUi() {
- val uri1 = Uri.parse("content://org.pkg.app/test.mp4")
- val uri2 = Uri.parse("content://org.pkg.app/test.pdf")
- val targetIntent =
- Intent(Intent.ACTION_SEND_MULTIPLE).apply {
- putExtra(
- Intent.EXTRA_STREAM,
- ArrayList<Uri>().apply {
- add(uri1)
- add(uri2)
- }
- )
- }
- whenever(contentResolver.getType(uri1)).thenReturn("video/mpeg4")
- whenever(contentResolver.getStreamTypes(uri1, "*/*")).thenReturn(arrayOf("image/png"))
- whenever(contentResolver.getType(uri2)).thenReturn("application/pdf")
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
- assertThat(testSubject.uriCount).isEqualTo(2)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri1)
- assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri1)
- verify(contentResolver, times(2)).getType(any())
- }
-
- @Test
- fun test_allNonImageUrisWithoutPreview_useFilePreviewUi() {
- val uri1 = Uri.parse("content://org.pkg.app/test.html")
- val uri2 = Uri.parse("content://org.pkg.app/test.pdf")
- val targetIntent =
- Intent(Intent.ACTION_SEND_MULTIPLE).apply {
- putExtra(
- Intent.EXTRA_STREAM,
- ArrayList<Uri>().apply {
- add(uri1)
- add(uri2)
- }
- )
- }
- whenever(contentResolver.getType(uri1)).thenReturn("text/html")
- whenever(contentResolver.getType(uri2)).thenReturn("application/pdf")
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
- assertThat(testSubject.uriCount).isEqualTo(2)
- assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri1)
- assertThat(testSubject.firstFileInfo?.previewUri).isNull()
- verify(contentResolver, times(2)).getType(any())
- }
-
- @Test
- fun test_imagePreviewFileInfoFlow_dataLoadedOnce() =
- testScope.runTest {
- val uri1 = Uri.parse("content://org.pkg.app/test.html")
- val uri2 = Uri.parse("content://org.pkg.app/test.pdf")
- val targetIntent =
- Intent(Intent.ACTION_SEND_MULTIPLE).apply {
- putExtra(
- Intent.EXTRA_STREAM,
- ArrayList<Uri>().apply {
- add(uri1)
- add(uri2)
- }
- )
- }
- whenever(contentResolver.getType(uri1)).thenReturn("text/html")
- whenever(contentResolver.getType(uri2)).thenReturn("application/pdf")
- whenever(contentResolver.getStreamTypes(uri1, "*/*"))
- .thenReturn(arrayOf("text/html", "image/jpeg"))
- whenever(contentResolver.getStreamTypes(uri2, "*/*"))
- .thenReturn(arrayOf("application/pdf", "image/png"))
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
-
- val fileInfoListOne = testSubject.imagePreviewFileInfoFlow.toList()
- val fileInfoListTwo = testSubject.imagePreviewFileInfoFlow.toList()
-
- assertThat(fileInfoListOne).hasSize(2)
- assertThat(fileInfoListOne).containsAtLeastElementsIn(fileInfoListTwo).inOrder()
-
- verify(contentResolver, times(1)).getType(uri1)
- verify(contentResolver, times(1)).getStreamTypes(uri1, "*/*")
- verify(contentResolver, times(1)).getType(uri2)
- verify(contentResolver, times(1)).getStreamTypes(uri2, "*/*")
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/contentpreview/TextContentPreviewUiTest.kt b/java/tests/src/com/android/intentresolver/contentpreview/TextContentPreviewUiTest.kt
deleted file mode 100644
index 35362401..00000000
--- a/java/tests/src/com/android/intentresolver/contentpreview/TextContentPreviewUiTest.kt
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.contentpreview
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.intentresolver.R
-import com.android.intentresolver.mock
-import com.android.intentresolver.whenever
-import com.android.intentresolver.widget.ActionRow
-import com.google.common.truth.Truth.assertThat
-import java.util.function.Consumer
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class TextContentPreviewUiTest {
- private val text = "Shared Text"
- private val title = "Preview Title"
- private val testScope = TestScope(EmptyCoroutineContext + UnconfinedTestDispatcher())
- private val actionFactory =
- object : ChooserContentPreviewUi.ActionFactory {
- override fun getEditButtonRunnable(): Runnable? = null
- override fun getCopyButtonRunnable(): Runnable? = null
- override fun createCustomActions(): List<ActionRow.Action> = emptyList()
- override fun getModifyShareAction(): ActionRow.Action? = null
- override fun getExcludeSharedTextAction(): Consumer<Boolean> = Consumer<Boolean> {}
- }
- private val imageLoader = mock<ImageLoader>()
- private val headlineGenerator =
- mock<HeadlineGenerator> { whenever(getTextHeadline(text)).thenReturn(text) }
-
- private val context
- get() = InstrumentationRegistry.getInstrumentation().context
-
- private val testSubject =
- TextContentPreviewUi(
- testScope,
- text,
- title,
- /*previewThumbnail=*/ null,
- actionFactory,
- imageLoader,
- headlineGenerator,
- )
-
- @Test
- fun test_display_headlineIsDisplayed() {
- val layoutInflater = LayoutInflater.from(context)
- val gridLayout = layoutInflater.inflate(R.layout.chooser_grid, null, false) as ViewGroup
-
- val previewView =
- testSubject.display(
- context.resources,
- layoutInflater,
- gridLayout,
- /*headlineViewParent=*/ null
- )
-
- assertThat(previewView).isNotNull()
- val headlineView = previewView?.findViewById<TextView>(R.id.headline)
- assertThat(headlineView).isNotNull()
- assertThat(headlineView?.text).isEqualTo(text)
- }
-
- @Test
- fun test_displayWithExternalHeaderView_externalHeaderIsDisplayed() {
- val layoutInflater = LayoutInflater.from(context)
- val gridLayout =
- layoutInflater.inflate(R.layout.chooser_grid_scrollable_preview, null, false)
- as ViewGroup
- val externalHeaderView =
- gridLayout.requireViewById<View>(R.id.chooser_headline_row_container)
-
- assertThat(externalHeaderView.findViewById<View>(R.id.headline)).isNull()
-
- val previewView =
- testSubject.display(context.resources, layoutInflater, gridLayout, externalHeaderView)
-
- assertThat(previewView).isNotNull()
- assertThat(previewView.findViewById<View>(R.id.headline)).isNull()
-
- val headlineView = externalHeaderView.findViewById<TextView>(R.id.headline)
- assertThat(headlineView).isNotNull()
- assertThat(headlineView?.text).isEqualTo(text)
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUiTest.kt b/java/tests/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUiTest.kt
deleted file mode 100644
index 7e07e0ca..00000000
--- a/java/tests/src/com/android/intentresolver/contentpreview/UnifiedContentPreviewUiTest.kt
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.contentpreview
-
-import android.net.Uri
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
-import com.android.intentresolver.R
-import com.android.intentresolver.mock
-import com.android.intentresolver.whenever
-import com.android.intentresolver.widget.ImagePreviewView.TransitionElementStatusCallback
-import com.google.common.truth.Truth
-import com.google.common.truth.Truth.assertWithMessage
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.asFlow
-import kotlinx.coroutines.flow.takeWhile
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-
-private const val IMAGE_HEADLINE = "Image Headline"
-private const val VIDEO_HEADLINE = "Video Headline"
-private const val FILES_HEADLINE = "Files Headline"
-
-@RunWith(AndroidJUnit4::class)
-class UnifiedContentPreviewUiTest {
- private val testScope = TestScope(EmptyCoroutineContext + UnconfinedTestDispatcher())
- private val actionFactory =
- mock<ChooserContentPreviewUi.ActionFactory> {
- whenever(createCustomActions()).thenReturn(emptyList())
- }
- private val imageLoader = mock<ImageLoader>()
- private val headlineGenerator =
- mock<HeadlineGenerator> {
- whenever(getImagesHeadline(anyInt())).thenReturn(IMAGE_HEADLINE)
- whenever(getVideosHeadline(anyInt())).thenReturn(VIDEO_HEADLINE)
- whenever(getFilesHeadline(anyInt())).thenReturn(FILES_HEADLINE)
- }
-
- private val context
- get() = getInstrumentation().context
-
- @Test
- fun test_displayImagesWithoutUriMetadata_showImagesHeadline() {
- testLoadingHeadline("image/*", files = null) { previewView ->
- verify(headlineGenerator, times(1)).getImagesHeadline(2)
- verifyPreviewHeadline(previewView, IMAGE_HEADLINE)
- }
- }
-
- @Test
- fun test_displayImagesWithoutUriMetadataExternalHeader_showImagesHeadline() {
- testLoadingExternalHeadline("image/*", files = null) { externalHeaderView ->
- verify(headlineGenerator, times(1)).getImagesHeadline(2)
- verifyPreviewHeadline(externalHeaderView, IMAGE_HEADLINE)
- }
- }
-
- @Test
- fun test_displayVideosWithoutUriMetadata_showImagesHeadline() {
- testLoadingHeadline("video/*", files = null) { previewView ->
- verify(headlineGenerator, times(1)).getVideosHeadline(2)
- verifyPreviewHeadline(previewView, VIDEO_HEADLINE)
- }
- }
-
- @Test
- fun test_displayVideosWithoutUriMetadataExternalHeader_showImagesHeadline() {
- testLoadingExternalHeadline("video/*", files = null) { externalHeaderView ->
- verify(headlineGenerator, times(1)).getVideosHeadline(2)
- verifyPreviewHeadline(externalHeaderView, VIDEO_HEADLINE)
- }
- }
-
- @Test
- fun test_displayDocumentsWithoutUriMetadata_showImagesHeadline() {
- testLoadingHeadline("application/pdf", files = null) { previewView ->
- verify(headlineGenerator, times(1)).getFilesHeadline(2)
- verifyPreviewHeadline(previewView, FILES_HEADLINE)
- }
- }
-
- @Test
- fun test_displayDocumentsWithoutUriMetadataExternalHeader_showImagesHeadline() {
- testLoadingExternalHeadline("application/pdf", files = null) { externalHeaderView ->
- verify(headlineGenerator, times(1)).getFilesHeadline(2)
- verifyPreviewHeadline(externalHeaderView, FILES_HEADLINE)
- }
- }
-
- @Test
- fun test_displayMixedContentWithoutUriMetadata_showImagesHeadline() {
- testLoadingHeadline("*/*", files = null) { previewView ->
- verify(headlineGenerator, times(1)).getFilesHeadline(2)
- verifyPreviewHeadline(previewView, FILES_HEADLINE)
- }
- }
-
- @Test
- fun test_displayMixedContentWithoutUriMetadataExternalHeader_showImagesHeadline() {
- testLoadingExternalHeadline("*/*", files = null) { externalHeader ->
- verify(headlineGenerator, times(1)).getFilesHeadline(2)
- verifyPreviewHeadline(externalHeader, FILES_HEADLINE)
- }
- }
-
- @Test
- fun test_displayImagesWithUriMetadataSet_showImagesHeadline() {
- val uri = Uri.parse("content://pkg.app/image.png")
- val files =
- listOf(
- FileInfo.Builder(uri).withMimeType("image/png").build(),
- FileInfo.Builder(uri).withMimeType("image/jpeg").build(),
- )
- testLoadingHeadline("image/*", files) { preivewView ->
- verify(headlineGenerator, times(1)).getImagesHeadline(2)
- verifyPreviewHeadline(preivewView, IMAGE_HEADLINE)
- }
- }
-
- @Test
- fun test_displayImagesWithUriMetadataSetExternalHeader_showImagesHeadline() {
- val uri = Uri.parse("content://pkg.app/image.png")
- val files =
- listOf(
- FileInfo.Builder(uri).withMimeType("image/png").build(),
- FileInfo.Builder(uri).withMimeType("image/jpeg").build(),
- )
- testLoadingExternalHeadline("image/*", files) { externalHeader ->
- verify(headlineGenerator, times(1)).getImagesHeadline(2)
- verifyPreviewHeadline(externalHeader, IMAGE_HEADLINE)
- }
- }
-
- @Test
- fun test_displayVideosWithUriMetadataSet_showImagesHeadline() {
- val uri = Uri.parse("content://pkg.app/image.png")
- val files =
- listOf(
- FileInfo.Builder(uri).withMimeType("video/mp4").build(),
- FileInfo.Builder(uri).withMimeType("video/mp4").build(),
- )
- testLoadingHeadline("video/*", files) { previewView ->
- verify(headlineGenerator, times(1)).getVideosHeadline(2)
- verifyPreviewHeadline(previewView, VIDEO_HEADLINE)
- }
- }
-
- @Test
- fun test_displayVideosWithUriMetadataSetExternalHeader_showImagesHeadline() {
- val uri = Uri.parse("content://pkg.app/image.png")
- val files =
- listOf(
- FileInfo.Builder(uri).withMimeType("video/mp4").build(),
- FileInfo.Builder(uri).withMimeType("video/mp4").build(),
- )
- testLoadingExternalHeadline("video/*", files) { externalHeader ->
- verify(headlineGenerator, times(1)).getVideosHeadline(2)
- verifyPreviewHeadline(externalHeader, VIDEO_HEADLINE)
- }
- }
-
- @Test
- fun test_displayImagesAndVideosWithUriMetadataSet_showImagesHeadline() {
- val uri = Uri.parse("content://pkg.app/image.png")
- val files =
- listOf(
- FileInfo.Builder(uri).withMimeType("image/png").build(),
- FileInfo.Builder(uri).withMimeType("video/mp4").build(),
- )
- testLoadingHeadline("*/*", files) { previewView ->
- verify(headlineGenerator, times(1)).getFilesHeadline(2)
- verifyPreviewHeadline(previewView, FILES_HEADLINE)
- }
- }
-
- @Test
- fun test_displayImagesAndVideosWithUriMetadataSetExternalHeader_showImagesHeadline() {
- val uri = Uri.parse("content://pkg.app/image.png")
- val files =
- listOf(
- FileInfo.Builder(uri).withMimeType("image/png").build(),
- FileInfo.Builder(uri).withMimeType("video/mp4").build(),
- )
- testLoadingExternalHeadline("*/*", files) { externalHeader ->
- verify(headlineGenerator, times(1)).getFilesHeadline(2)
- verifyPreviewHeadline(externalHeader, FILES_HEADLINE)
- }
- }
-
- @Test
- fun test_displayDocumentsWithUriMetadataSet_showImagesHeadline() {
- val uri = Uri.parse("content://pkg.app/image.png")
- val files =
- listOf(
- FileInfo.Builder(uri).withMimeType("application/pdf").build(),
- FileInfo.Builder(uri).withMimeType("application/pdf").build(),
- )
- testLoadingHeadline("application/pdf", files) { previewView ->
- verify(headlineGenerator, times(1)).getFilesHeadline(2)
- verifyPreviewHeadline(previewView, FILES_HEADLINE)
- }
- }
-
- @Test
- fun test_displayDocumentsWithUriMetadataSetExternalHeader_showImagesHeadline() {
- val uri = Uri.parse("content://pkg.app/image.png")
- val files =
- listOf(
- FileInfo.Builder(uri).withMimeType("application/pdf").build(),
- FileInfo.Builder(uri).withMimeType("application/pdf").build(),
- )
- testLoadingExternalHeadline("application/pdf", files) { externalHeader ->
- verify(headlineGenerator, times(1)).getFilesHeadline(2)
- verifyPreviewHeadline(externalHeader, FILES_HEADLINE)
- }
- }
-
- private fun testLoadingHeadline(
- intentMimeType: String,
- files: List<FileInfo>?,
- verificationBlock: (ViewGroup?) -> Unit,
- ) {
- testScope.runTest {
- val endMarker = FileInfo.Builder(Uri.EMPTY).build()
- val emptySourceFlow = MutableSharedFlow<FileInfo>(replay = 1)
- val testSubject =
- UnifiedContentPreviewUi(
- testScope,
- /*isSingleImage=*/ false,
- intentMimeType,
- actionFactory,
- imageLoader,
- DefaultMimeTypeClassifier,
- object : TransitionElementStatusCallback {
- override fun onTransitionElementReady(name: String) = Unit
- override fun onAllTransitionElementsReady() = Unit
- },
- files?.let { it.asFlow() } ?: emptySourceFlow.takeWhile { it !== endMarker },
- /*itemCount=*/ 2,
- headlineGenerator
- )
- val layoutInflater = LayoutInflater.from(context)
- val gridLayout = layoutInflater.inflate(R.layout.chooser_grid, null, false) as ViewGroup
-
- val previewView =
- testSubject.display(
- context.resources,
- LayoutInflater.from(context),
- gridLayout,
- /*headlineViewParent=*/ null
- )
- emptySourceFlow.tryEmit(endMarker)
-
- verificationBlock(previewView)
- }
- }
-
- private fun testLoadingExternalHeadline(
- intentMimeType: String,
- files: List<FileInfo>?,
- verificationBlock: (View?) -> Unit,
- ) {
- testScope.runTest {
- val endMarker = FileInfo.Builder(Uri.EMPTY).build()
- val emptySourceFlow = MutableSharedFlow<FileInfo>(replay = 1)
- val testSubject =
- UnifiedContentPreviewUi(
- testScope,
- /*isSingleImage=*/ false,
- intentMimeType,
- actionFactory,
- imageLoader,
- DefaultMimeTypeClassifier,
- object : TransitionElementStatusCallback {
- override fun onTransitionElementReady(name: String) = Unit
- override fun onAllTransitionElementsReady() = Unit
- },
- files?.let { it.asFlow() } ?: emptySourceFlow.takeWhile { it !== endMarker },
- /*itemCount=*/ 2,
- headlineGenerator
- )
- val layoutInflater = LayoutInflater.from(context)
- val gridLayout =
- layoutInflater.inflate(R.layout.chooser_grid_scrollable_preview, null, false)
- as ViewGroup
- val externalHeaderView =
- gridLayout.requireViewById<View>(R.id.chooser_headline_row_container)
-
- assertWithMessage("External headline should not be inflated by default")
- .that(externalHeaderView.findViewById<View>(R.id.headline))
- .isNull()
-
- val previewView =
- testSubject.display(
- context.resources,
- LayoutInflater.from(context),
- gridLayout,
- externalHeaderView,
- )
-
- emptySourceFlow.tryEmit(endMarker)
-
- verifyInternalHeadlineAbsence(previewView)
- verificationBlock(externalHeaderView)
- }
- }
-
- private fun verifyPreviewHeadline(headerViewParent: View?, expectedText: String) {
- Truth.assertThat(headerViewParent).isNotNull()
- val headlineView = headerViewParent?.findViewById<TextView>(R.id.headline)
- Truth.assertThat(headlineView).isNotNull()
- Truth.assertThat(headlineView?.text).isEqualTo(expectedText)
- }
-
- private fun verifyInternalHeadlineAbsence(previewView: ViewGroup?) {
- assertWithMessage("Preview parent should not be null").that(previewView).isNotNull()
- assertWithMessage(
- "Preview headline should not be inflated when an external headline is used"
- )
- .that(previewView?.findViewById<View>(R.id.headline))
- .isNull()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/emptystate/CompositeEmptyStateProviderTest.kt b/java/tests/src/com/android/intentresolver/emptystate/CompositeEmptyStateProviderTest.kt
deleted file mode 100644
index 4c05dfb1..00000000
--- a/java/tests/src/com/android/intentresolver/emptystate/CompositeEmptyStateProviderTest.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.emptystate
-
-import com.android.intentresolver.ResolverListAdapter
-import com.android.intentresolver.mock
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-
-class CompositeEmptyStateProviderTest {
- val listAdapter = mock<ResolverListAdapter>()
-
- val emptyState1 = object : EmptyState {}
- val emptyState2 = object : EmptyState {}
-
- val positiveEmptyStateProvider1 =
- object : EmptyStateProvider {
- override fun getEmptyState(listAdapter: ResolverListAdapter) = emptyState1
- }
- val positiveEmptyStateProvider2 =
- object : EmptyStateProvider {
- override fun getEmptyState(listAdapter: ResolverListAdapter) = emptyState2
- }
- val nullEmptyStateProvider =
- object : EmptyStateProvider {
- override fun getEmptyState(listAdapter: ResolverListAdapter) = null
- }
-
- @Test
- fun testComposedProvider_returnsFirstEmptyStateInOrder() {
- val provider =
- CompositeEmptyStateProvider(
- nullEmptyStateProvider,
- positiveEmptyStateProvider1,
- positiveEmptyStateProvider2
- )
- assertThat(provider.getEmptyState(listAdapter)).isSameInstanceAs(emptyState1)
- }
-
- @Test
- fun testComposedProvider_allProvidersReturnNull_composedResultIsNull() {
- val provider = CompositeEmptyStateProvider(nullEmptyStateProvider)
- assertThat(provider.getEmptyState(listAdapter)).isNull()
- }
-
- @Test
- fun testComposedProvider_noEmptyStateIfNoDelegateProviders() {
- val provider = CompositeEmptyStateProvider()
- assertThat(provider.getEmptyState(listAdapter)).isNull()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/emptystate/CrossProfileIntentsCheckerTest.kt b/java/tests/src/com/android/intentresolver/emptystate/CrossProfileIntentsCheckerTest.kt
deleted file mode 100644
index 2bcddf59..00000000
--- a/java/tests/src/com/android/intentresolver/emptystate/CrossProfileIntentsCheckerTest.kt
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.emptystate
-
-import android.content.ContentResolver
-import android.content.Intent
-import android.content.pm.IPackageManager
-import com.android.intentresolver.mock
-import com.android.intentresolver.whenever
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.mockito.Mockito.any
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.eq
-import org.mockito.Mockito.nullable
-
-class CrossProfileIntentsCheckerTest {
- private val PERSONAL_USER_ID = 10
- private val WORK_USER_ID = 20
-
- private val contentResolver = mock<ContentResolver>()
-
- @Test
- fun testChecker_hasCrossProfileIntents() {
- val packageManager =
- mock<IPackageManager> {
- whenever(
- canForwardTo(
- any(Intent::class.java),
- nullable(String::class.java),
- eq(PERSONAL_USER_ID),
- eq(WORK_USER_ID)
- )
- )
- .thenReturn(true)
- }
- val checker = CrossProfileIntentsChecker(contentResolver, packageManager)
- val intents = listOf(Intent())
- assertThat(checker.hasCrossProfileIntents(intents, PERSONAL_USER_ID, WORK_USER_ID)).isTrue()
- }
-
- @Test
- fun testChecker_noCrossProfileIntents() {
- val packageManager =
- mock<IPackageManager> {
- whenever(
- canForwardTo(
- any(Intent::class.java),
- nullable(String::class.java),
- anyInt(),
- anyInt()
- )
- )
- .thenReturn(false)
- }
- val checker = CrossProfileIntentsChecker(contentResolver, packageManager)
- val intents = listOf(Intent())
- assertThat(checker.hasCrossProfileIntents(intents, PERSONAL_USER_ID, WORK_USER_ID))
- .isFalse()
- }
-
- @Test
- fun testChecker_noIntents() {
- val packageManager = mock<IPackageManager>()
- val checker = CrossProfileIntentsChecker(contentResolver, packageManager)
- val intents = listOf<Intent>()
- assertThat(checker.hasCrossProfileIntents(intents, PERSONAL_USER_ID, WORK_USER_ID))
- .isFalse()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/emptystate/EmptyStateUiHelperTest.kt b/java/tests/src/com/android/intentresolver/emptystate/EmptyStateUiHelperTest.kt
deleted file mode 100644
index bc5545db..00000000
--- a/java/tests/src/com/android/intentresolver/emptystate/EmptyStateUiHelperTest.kt
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.emptystate
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import androidx.test.platform.app.InstrumentationRegistry
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-
-class EmptyStateUiHelperTest {
- private val context = InstrumentationRegistry.getInstrumentation().getContext()
-
- lateinit var rootContainer: ViewGroup
- lateinit var emptyStateTitleView: View
- lateinit var emptyStateSubtitleView: View
- lateinit var emptyStateButtonView: View
- lateinit var emptyStateProgressView: View
- lateinit var emptyStateDefaultTextView: View
- lateinit var emptyStateContainerView: View
- lateinit var emptyStateRootView: View
- lateinit var emptyStateUiHelper: EmptyStateUiHelper
-
- @Before
- fun setup() {
- rootContainer = FrameLayout(context)
- LayoutInflater.from(context)
- .inflate(
- com.android.intentresolver.R.layout.resolver_list_per_profile,
- rootContainer,
- true
- )
- emptyStateRootView =
- rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state)
- emptyStateTitleView =
- rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_title)
- emptyStateSubtitleView = rootContainer.requireViewById(
- com.android.internal.R.id.resolver_empty_state_subtitle)
- emptyStateButtonView = rootContainer.requireViewById(
- com.android.internal.R.id.resolver_empty_state_button)
- emptyStateProgressView = rootContainer.requireViewById(
- com.android.internal.R.id.resolver_empty_state_progress)
- emptyStateDefaultTextView =
- rootContainer.requireViewById(com.android.internal.R.id.empty)
- emptyStateContainerView = rootContainer.requireViewById(
- com.android.internal.R.id.resolver_empty_state_container)
- emptyStateUiHelper = EmptyStateUiHelper(rootContainer)
- }
-
- @Test
- fun testResetViewVisibilities() {
- // First set each view's visibility to differ from the expected "reset" state so we can then
- // assert that they're all reset afterward.
- // TODO: for historic reasons "reset" doesn't cover `emptyStateContainerView`; should it?
- emptyStateRootView.visibility = View.GONE
- emptyStateTitleView.visibility = View.GONE
- emptyStateSubtitleView.visibility = View.GONE
- emptyStateButtonView.visibility = View.VISIBLE
- emptyStateProgressView.visibility = View.VISIBLE
- emptyStateDefaultTextView.visibility = View.VISIBLE
-
- emptyStateUiHelper.resetViewVisibilities()
-
- assertThat(emptyStateRootView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateTitleView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateSubtitleView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateButtonView.visibility).isEqualTo(View.INVISIBLE)
- assertThat(emptyStateProgressView.visibility).isEqualTo(View.GONE)
- assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
- }
-
- @Test
- fun testShowSpinner() {
- emptyStateTitleView.visibility = View.VISIBLE
- emptyStateButtonView.visibility = View.VISIBLE
- emptyStateProgressView.visibility = View.GONE
- emptyStateDefaultTextView.visibility = View.VISIBLE
-
- emptyStateUiHelper.showSpinner()
-
- // TODO: should this cover any other views? Subtitle?
- assertThat(emptyStateTitleView.visibility).isEqualTo(View.INVISIBLE)
- assertThat(emptyStateButtonView.visibility).isEqualTo(View.INVISIBLE)
- assertThat(emptyStateProgressView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
- }
-
- @Test
- fun testHide() {
- emptyStateRootView.visibility = View.VISIBLE
-
- emptyStateUiHelper.hide()
-
- assertThat(emptyStateRootView.visibility).isEqualTo(View.GONE)
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/logging/EventLogImplTest.java b/java/tests/src/com/android/intentresolver/logging/EventLogImplTest.java
deleted file mode 100644
index d75ea99b..00000000
--- a/java/tests/src/com/android/intentresolver/logging/EventLogImplTest.java
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.logging;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.AdditionalMatchers.gt;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-import android.content.Intent;
-import android.metrics.LogMaker;
-
-import com.android.intentresolver.logging.EventLogImpl.SharesheetStandardEvent;
-import com.android.intentresolver.logging.EventLogImpl.SharesheetStartedEvent;
-import com.android.intentresolver.logging.EventLogImpl.SharesheetTargetSelectedEvent;
-import com.android.intentresolver.contentpreview.ContentPreviewType;
-import com.android.internal.logging.InstanceId;
-import com.android.internal.logging.InstanceIdSequence;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLogger.UiEventEnum;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.FrameworkStatsLog;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-@RunWith(MockitoJUnitRunner.class)
-public final class EventLogImplTest {
- @Mock private UiEventLogger mUiEventLog;
- @Mock private FrameworkStatsLogger mFrameworkLog;
- @Mock private MetricsLogger mMetricsLogger;
-
- private EventLogImpl mChooserLogger;
-
- private final InstanceIdSequence mSequence = EventLogImpl.newIdSequence();
-
- @Before
- public void setUp() {
- mChooserLogger = new EventLogImpl(mUiEventLog, mFrameworkLog, mMetricsLogger,
- mSequence.newInstanceId());
- }
-
- @After
- public void tearDown() {
- verifyNoMoreInteractions(mUiEventLog);
- verifyNoMoreInteractions(mFrameworkLog);
- verifyNoMoreInteractions(mMetricsLogger);
- }
-
- @Test
- public void testLogChooserActivityShown_personalProfile() {
- final boolean isWorkProfile = false;
- final String mimeType = "application/TestType";
- final long systemCost = 456;
-
- mChooserLogger.logChooserActivityShown(isWorkProfile, mimeType, systemCost);
-
- ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class);
- verify(mMetricsLogger).write(eventCaptor.capture());
- LogMaker event = eventCaptor.getValue();
-
- assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN);
- assertThat(event.getSubtype()).isEqualTo(MetricsEvent.PARENT_PROFILE);
- assertThat(event.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE)).isEqualTo(mimeType);
- assertThat(event.getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS))
- .isEqualTo(systemCost);
- }
-
- @Test
- public void testLogChooserActivityShown_workProfile() {
- final boolean isWorkProfile = true;
- final String mimeType = "application/TestType";
- final long systemCost = 456;
-
- mChooserLogger.logChooserActivityShown(isWorkProfile, mimeType, systemCost);
-
- ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class);
- verify(mMetricsLogger).write(eventCaptor.capture());
- LogMaker event = eventCaptor.getValue();
-
- assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN);
- assertThat(event.getSubtype()).isEqualTo(MetricsEvent.MANAGED_PROFILE);
- assertThat(event.getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE)).isEqualTo(mimeType);
- assertThat(event.getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS))
- .isEqualTo(systemCost);
- }
-
- @Test
- public void testLogShareStarted() {
- final String packageName = "com.test.foo";
- final String mimeType = "text/plain";
- final int appProvidedDirectTargets = 123;
- final int appProvidedAppTargets = 456;
- final boolean workProfile = true;
- final int previewType = ContentPreviewType.CONTENT_PREVIEW_FILE;
- final String intentAction = Intent.ACTION_SENDTO;
- final int numCustomActions = 3;
- final boolean modifyShareProvided = true;
-
- mChooserLogger.logShareStarted(
- packageName,
- mimeType,
- appProvidedDirectTargets,
- appProvidedAppTargets,
- workProfile,
- previewType,
- intentAction,
- numCustomActions,
- modifyShareProvided);
-
- verify(mFrameworkLog).write(
- eq(FrameworkStatsLog.SHARESHEET_STARTED),
- eq(SharesheetStartedEvent.SHARE_STARTED.getId()),
- eq(packageName),
- /* instanceId=*/ gt(0),
- eq(mimeType),
- eq(appProvidedDirectTargets),
- eq(appProvidedAppTargets),
- eq(workProfile),
- eq(FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_FILE),
- eq(FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SENDTO),
- /* custom actions provided */ eq(numCustomActions),
- /* reselection action provided */ eq(modifyShareProvided));
- }
-
- @Test
- public void testLogShareTargetSelected() {
- final int targetType = EventLogImpl.SELECTION_TYPE_SERVICE;
- final String packageName = "com.test.foo";
- final int positionPicked = 123;
- final int directTargetAlsoRanked = -1;
- final int callerTargetCount = 0;
- final boolean isPinned = true;
- final boolean isSuccessfullySelected = true;
- final long selectionCost = 456;
-
- mChooserLogger.logShareTargetSelected(
- targetType,
- packageName,
- positionPicked,
- directTargetAlsoRanked,
- callerTargetCount,
- /* directTargetHashed= */ null,
- isPinned,
- isSuccessfullySelected,
- selectionCost);
-
- verify(mFrameworkLog).write(
- eq(FrameworkStatsLog.RANKING_SELECTED),
- eq(SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()),
- eq(packageName),
- /* instanceId=*/ gt(0),
- eq(positionPicked),
- eq(isPinned));
-
- ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class);
- verify(mMetricsLogger).write(eventCaptor.capture());
- LogMaker event = eventCaptor.getValue();
- assertThat(event.getCategory()).isEqualTo(
- MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET);
- assertThat(event.getSubtype()).isEqualTo(positionPicked);
- }
-
- @Test
- public void testLogActionSelected() {
- mChooserLogger.logActionSelected(EventLogImpl.SELECTION_TYPE_COPY);
-
- verify(mFrameworkLog).write(
- eq(FrameworkStatsLog.RANKING_SELECTED),
- eq(SharesheetTargetSelectedEvent.SHARESHEET_COPY_TARGET_SELECTED.getId()),
- eq(""),
- /* instanceId=*/ gt(0),
- eq(-1),
- eq(false));
-
- ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class);
- verify(mMetricsLogger).write(eventCaptor.capture());
- LogMaker event = eventCaptor.getValue();
- assertThat(event.getCategory()).isEqualTo(
- MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET);
- assertThat(event.getSubtype()).isEqualTo(1);
- }
-
- @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;
-
- mChooserLogger.logDirectShareTargetReceived(category, latency);
-
- ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class);
- verify(mMetricsLogger).write(eventCaptor.capture());
- LogMaker event = eventCaptor.getValue();
- assertThat(event.getCategory()).isEqualTo(category);
- assertThat(event.getSubtype()).isEqualTo(latency);
- }
-
- @Test
- public void testLogActionShareWithPreview() {
- final int previewType = ContentPreviewType.CONTENT_PREVIEW_TEXT;
-
- mChooserLogger.logActionShareWithPreview(previewType);
-
- ArgumentCaptor<LogMaker> eventCaptor = ArgumentCaptor.forClass(LogMaker.class);
- verify(mMetricsLogger).write(eventCaptor.capture());
- LogMaker event = eventCaptor.getValue();
- assertThat(event.getCategory()).isEqualTo(MetricsEvent.ACTION_SHARE_WITH_PREVIEW);
- assertThat(event.getSubtype()).isEqualTo(previewType);
- }
-
- @Test
- public void testLogSharesheetTriggered() {
- mChooserLogger.logSharesheetTriggered();
- verify(mUiEventLog).logWithInstanceId(
- eq(SharesheetStandardEvent.SHARESHEET_TRIGGERED), eq(0), isNull(), any());
- }
-
- @Test
- public void testLogSharesheetAppLoadComplete() {
- mChooserLogger.logSharesheetAppLoadComplete();
- verify(mUiEventLog).logWithInstanceId(
- eq(SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE), eq(0), isNull(), any());
- }
-
- @Test
- public void testLogSharesheetDirectLoadComplete() {
- mChooserLogger.logSharesheetDirectLoadComplete();
- verify(mUiEventLog).logWithInstanceId(
- eq(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE),
- eq(0),
- isNull(),
- any());
- }
-
- @Test
- public void testLogSharesheetDirectLoadTimeout() {
- mChooserLogger.logSharesheetDirectLoadTimeout();
- verify(mUiEventLog).logWithInstanceId(
- eq(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_TIMEOUT), eq(0), isNull(), any());
- }
-
- @Test
- public void testLogSharesheetProfileChanged() {
- mChooserLogger.logSharesheetProfileChanged();
- verify(mUiEventLog).logWithInstanceId(
- eq(SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED), eq(0), isNull(), any());
- }
-
- @Test
- public void testLogSharesheetExpansionChanged_collapsed() {
- mChooserLogger.logSharesheetExpansionChanged(/* isCollapsed=*/ true);
- verify(mUiEventLog).logWithInstanceId(
- eq(SharesheetStandardEvent.SHARESHEET_COLLAPSED), eq(0), isNull(), any());
- }
-
- @Test
- public void testLogSharesheetExpansionChanged_expanded() {
- mChooserLogger.logSharesheetExpansionChanged(/* isCollapsed=*/ false);
- verify(mUiEventLog).logWithInstanceId(
- eq(SharesheetStandardEvent.SHARESHEET_EXPANDED), eq(0), isNull(), any());
- }
-
- @Test
- public void testLogSharesheetAppShareRankingTimeout() {
- mChooserLogger.logSharesheetAppShareRankingTimeout();
- verify(mUiEventLog).logWithInstanceId(
- eq(SharesheetStandardEvent.SHARESHEET_APP_SHARE_RANKING_TIMEOUT),
- eq(0),
- isNull(),
- any());
- }
-
- @Test
- public void testLogSharesheetEmptyDirectShareRow() {
- mChooserLogger.logSharesheetEmptyDirectShareRow();
- verify(mUiEventLog).logWithInstanceId(
- eq(SharesheetStandardEvent.SHARESHEET_EMPTY_DIRECT_SHARE_ROW),
- eq(0),
- isNull(),
- any());
- }
-
- @Test
- public void testDifferentLoggerInstancesUseDifferentInstanceIds() {
- ArgumentCaptor<Integer> idIntCaptor = ArgumentCaptor.forClass(Integer.class);
- EventLogImpl chooserLogger2 =
- new EventLogImpl(mUiEventLog, mFrameworkLog, mMetricsLogger,
- mSequence.newInstanceId());
-
- final int targetType = EventLogImpl.SELECTION_TYPE_COPY;
- final String packageName = "com.test.foo";
- final int positionPicked = 123;
- final int directTargetAlsoRanked = -1;
- final int callerTargetCount = 0;
- final boolean isPinned = true;
- final boolean isSuccessfullySelected = true;
- final long selectionCost = 456;
-
- mChooserLogger.logShareTargetSelected(
- targetType,
- packageName,
- positionPicked,
- directTargetAlsoRanked,
- callerTargetCount,
- /* directTargetHashed= */ null,
- isPinned,
- isSuccessfullySelected,
- selectionCost);
-
- chooserLogger2.logShareTargetSelected(
- targetType,
- packageName,
- positionPicked,
- directTargetAlsoRanked,
- callerTargetCount,
- /* directTargetHashed= */ null,
- isPinned,
- isSuccessfullySelected,
- selectionCost);
-
- verify(mFrameworkLog, times(2)).write(
- anyInt(), anyInt(), anyString(), idIntCaptor.capture(), anyInt(), anyBoolean());
-
- int id1 = idIntCaptor.getAllValues().get(0);
- int id2 = idIntCaptor.getAllValues().get(1);
-
- assertThat(id1).isGreaterThan(0);
- assertThat(id2).isGreaterThan(0);
- assertThat(id1).isNotEqualTo(id2);
- }
-
- @Test
- public void testUiAndFrameworkEventsUseSameInstanceIdForSameLoggerInstance() {
- ArgumentCaptor<Integer> idIntCaptor = ArgumentCaptor.forClass(Integer.class);
- ArgumentCaptor<InstanceId> idObjectCaptor = ArgumentCaptor.forClass(InstanceId.class);
-
- final int targetType = EventLogImpl.SELECTION_TYPE_COPY;
- final String packageName = "com.test.foo";
- final int positionPicked = 123;
- final int directTargetAlsoRanked = -1;
- final int callerTargetCount = 0;
- final boolean isPinned = true;
- final boolean isSuccessfullySelected = true;
- final long selectionCost = 456;
-
- mChooserLogger.logShareTargetSelected(
- targetType,
- packageName,
- positionPicked,
- directTargetAlsoRanked,
- callerTargetCount,
- /* directTargetHashed= */ null,
- isPinned,
- isSuccessfullySelected,
- selectionCost);
-
- verify(mFrameworkLog).write(
- anyInt(), anyInt(), anyString(), idIntCaptor.capture(), anyInt(), anyBoolean());
-
- mChooserLogger.logSharesheetTriggered();
- verify(mUiEventLog).logWithInstanceId(
- any(UiEventEnum.class), anyInt(), any(), idObjectCaptor.capture());
-
- assertThat(idIntCaptor.getValue()).isGreaterThan(0);
- assertThat(idObjectCaptor.getValue().getId()).isEqualTo(idIntCaptor.getValue());
- }
-
- @Test
- public void testTargetSelectionCategories() {
- assertThat(EventLogImpl.getTargetSelectionCategory(
- EventLogImpl.SELECTION_TYPE_SERVICE))
- .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET);
- assertThat(EventLogImpl.getTargetSelectionCategory(
- EventLogImpl.SELECTION_TYPE_APP))
- .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET);
- assertThat(EventLogImpl.getTargetSelectionCategory(
- EventLogImpl.SELECTION_TYPE_STANDARD))
- .isEqualTo(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET);
- assertThat(EventLogImpl.getTargetSelectionCategory(
- EventLogImpl.SELECTION_TYPE_COPY)).isEqualTo(0);
- assertThat(EventLogImpl.getTargetSelectionCategory(
- EventLogImpl.SELECTION_TYPE_NEARBY)).isEqualTo(0);
- assertThat(EventLogImpl.getTargetSelectionCategory(
- EventLogImpl.SELECTION_TYPE_EDIT)).isEqualTo(0);
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/logging/FakeEventLog.kt b/java/tests/src/com/android/intentresolver/logging/FakeEventLog.kt
deleted file mode 100644
index 9ed47db6..00000000
--- a/java/tests/src/com/android/intentresolver/logging/FakeEventLog.kt
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.logging
-
-import android.net.Uri
-import android.util.HashedStringCache
-import android.util.Log
-import com.android.internal.logging.InstanceId
-import javax.inject.Inject
-
-private const val TAG = "EventLog"
-private const val LOG = true
-
-/** A fake EventLog. */
-class FakeEventLog @Inject constructor(private val instanceId: InstanceId) : EventLog {
-
- var chooserActivityShown: ChooserActivityShown? = null
- var actionSelected: ActionSelected? = null
- var customActionSelected: CustomActionSelected? = null
- var actionShareWithPreview: ActionShareWithPreview? = null
- val shareTargetSelected: MutableList<ShareTargetSelected> = mutableListOf()
-
- private fun log(message: () -> Any?) {
- if (LOG) {
- Log.d(TAG, "[%04x] ".format(instanceId.id) + message())
- }
- }
-
- override fun logChooserActivityShown(
- isWorkProfile: Boolean,
- targetMimeType: String?,
- systemCost: Long
- ) {
- chooserActivityShown = ChooserActivityShown(isWorkProfile, targetMimeType, systemCost)
- log { chooserActivityShown }
- }
-
- override fun logShareStarted(
- packageName: String?,
- mimeType: String?,
- appProvidedDirect: Int,
- appProvidedApp: Int,
- isWorkprofile: Boolean,
- previewType: Int,
- intent: String?,
- customActionCount: Int,
- modifyShareActionProvided: Boolean
- ) {
- log {
- ShareStarted(
- packageName,
- mimeType,
- appProvidedDirect,
- appProvidedApp,
- isWorkprofile,
- previewType,
- intent,
- customActionCount,
- modifyShareActionProvided
- )
- }
- }
-
- override fun logCustomActionSelected(positionPicked: Int) {
- customActionSelected = CustomActionSelected(positionPicked)
- log { "logCustomActionSelected(positionPicked=$positionPicked)" }
- }
-
- override fun logShareTargetSelected(
- targetType: Int,
- packageName: String?,
- positionPicked: Int,
- directTargetAlsoRanked: Int,
- numCallerProvided: Int,
- directTargetHashed: HashedStringCache.HashResult?,
- isPinned: Boolean,
- successfullySelected: Boolean,
- selectionCost: Long
- ) {
- shareTargetSelected.add(
- ShareTargetSelected(
- targetType,
- packageName,
- positionPicked,
- directTargetAlsoRanked,
- numCallerProvided,
- directTargetHashed,
- isPinned,
- successfullySelected,
- selectionCost
- )
- )
- log { shareTargetSelected.last() }
- shareTargetSelected.limitSize(10)
- }
-
- private fun MutableList<*>.limitSize(n: Int) {
- while (size > n) {
- removeFirst()
- }
- }
-
- override fun logDirectShareTargetReceived(category: Int, latency: Int) {
- log { "logDirectShareTargetReceived(category=$category, latency=$latency)" }
- }
-
- override fun logActionShareWithPreview(previewType: Int) {
- actionShareWithPreview = ActionShareWithPreview(previewType)
- log { actionShareWithPreview }
- }
-
- override fun logActionSelected(targetType: Int) {
- actionSelected = ActionSelected(targetType)
- log { actionSelected }
- }
-
- override fun logContentPreviewWarning(uri: Uri?) {
- log { "logContentPreviewWarning(uri=$uri)" }
- }
-
- override fun logSharesheetTriggered() {
- log { "logSharesheetTriggered()" }
- }
-
- override fun logSharesheetAppLoadComplete() {
- log { "logSharesheetAppLoadComplete()" }
- }
-
- override fun logSharesheetDirectLoadComplete() {
- log { "logSharesheetAppLoadComplete()" }
- }
-
- override fun logSharesheetDirectLoadTimeout() {
- log { "logSharesheetDirectLoadTimeout()" }
- }
-
- override fun logSharesheetProfileChanged() {
- log { "logSharesheetProfileChanged()" }
- }
-
- override fun logSharesheetExpansionChanged(isCollapsed: Boolean) {
- log { "logSharesheetExpansionChanged(isCollapsed=$isCollapsed)" }
- }
-
- override fun logSharesheetAppShareRankingTimeout() {
- log { "logSharesheetAppShareRankingTimeout()" }
- }
-
- override fun logSharesheetEmptyDirectShareRow() {
- log { "logSharesheetEmptyDirectShareRow()" }
- }
-
- data class ActionSelected(val targetType: Int)
- data class CustomActionSelected(val positionPicked: Int)
- data class ActionShareWithPreview(val previewType: Int)
- data class ChooserActivityShown(
- val isWorkProfile: Boolean,
- val targetMimeType: String?,
- val systemCost: Long
- )
- data class ShareStarted(
- val packageName: String?,
- val mimeType: String?,
- val appProvidedDirect: Int,
- val appProvidedApp: Int,
- val isWorkprofile: Boolean,
- val previewType: Int,
- val intent: String?,
- val customActionCount: Int,
- val modifyShareActionProvided: Boolean
- )
- data class ShareTargetSelected(
- val targetType: Int,
- val packageName: String?,
- val positionPicked: Int,
- val directTargetAlsoRanked: Int,
- val numCallerProvided: Int,
- val directTargetHashed: HashedStringCache.HashResult?,
- val pinned: Boolean,
- val successfullySelected: Boolean,
- val selectionCost: Long
- )
-}
diff --git a/java/tests/src/com/android/intentresolver/logging/FakeFrameworkStatsLogger.kt b/java/tests/src/com/android/intentresolver/logging/FakeFrameworkStatsLogger.kt
deleted file mode 100644
index dcf8d23f..00000000
--- a/java/tests/src/com/android/intentresolver/logging/FakeFrameworkStatsLogger.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.android.intentresolver.logging
-/*
- * 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.
- */
-
-import com.android.internal.util.FrameworkStatsLog
-
-internal data class ShareSheetStarted(
- val frameworkEventId: Int = FrameworkStatsLog.SHARESHEET_STARTED,
- val appEventId: Int,
- val packageName: String?,
- val instanceId: Int,
- val mimeType: String?,
- val numAppProvidedDirectTargets: Int,
- val numAppProvidedAppTargets: Int,
- val isWorkProfile: Boolean,
- val previewType: Int,
- val intentType: Int,
- val numCustomActions: Int,
- val modifyShareActionProvided: Boolean
-)
-
-internal data class RankingSelected(
- val frameworkEventId: Int = FrameworkStatsLog.RANKING_SELECTED,
- val appEventId: Int,
- val packageName: String?,
- val instanceId: Int,
- val positionPicked: Int,
- val isPinned: Boolean
-)
-
-internal class FakeFrameworkStatsLogger : FrameworkStatsLogger {
- var shareSheetStarted: ShareSheetStarted? = null
- var rankingSelected: RankingSelected? = null
- override fun write(
- frameworkEventId: Int,
- appEventId: Int,
- packageName: String?,
- instanceId: Int,
- mimeType: String?,
- numAppProvidedDirectTargets: Int,
- numAppProvidedAppTargets: Int,
- isWorkProfile: Boolean,
- previewType: Int,
- intentType: Int,
- numCustomActions: Int,
- modifyShareActionProvided: Boolean
- ) {
- shareSheetStarted =
- ShareSheetStarted(
- frameworkEventId,
- appEventId,
- packageName,
- instanceId,
- mimeType,
- numAppProvidedDirectTargets,
- numAppProvidedAppTargets,
- isWorkProfile,
- previewType,
- intentType,
- numCustomActions,
- modifyShareActionProvided
- )
- }
- override fun write(
- frameworkEventId: Int,
- appEventId: Int,
- packageName: String?,
- instanceId: Int,
- positionPicked: Int,
- isPinned: Boolean
- ) {
- rankingSelected =
- RankingSelected(
- frameworkEventId,
- appEventId,
- packageName,
- instanceId,
- positionPicked,
- isPinned
- )
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/logging/TestEventLogModule.kt b/java/tests/src/com/android/intentresolver/logging/TestEventLogModule.kt
deleted file mode 100644
index cd808af4..00000000
--- a/java/tests/src/com/android/intentresolver/logging/TestEventLogModule.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.logging
-
-import com.android.internal.logging.InstanceId
-import com.android.internal.logging.InstanceIdSequence
-import dagger.Binds
-import dagger.Module
-import dagger.Provides
-import dagger.hilt.android.components.ActivityComponent
-import dagger.hilt.android.scopes.ActivityScoped
-import dagger.hilt.testing.TestInstallIn
-
-/** Binds a [FakeEventLog] as [EventLog] in tests. */
-@Module
-@TestInstallIn(components = [ActivityComponent::class], replaces = [EventLogModule::class])
-interface TestEventLogModule {
-
- @Binds @ActivityScoped fun fakeEventLog(impl: FakeEventLog): EventLog
-
- companion object {
- @Provides
- fun instanceId(sequence: InstanceIdSequence): InstanceId = sequence.newInstanceId()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java b/java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java
deleted file mode 100644
index 2140a67d..00000000
--- a/java/tests/src/com/android/intentresolver/model/AbstractResolverComparatorTest.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2019 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.model;
-
-import static junit.framework.Assert.assertEquals;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ResolveInfo;
-import android.os.Message;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.intentresolver.ResolvedComponentInfo;
-import com.android.intentresolver.chooser.TargetInfo;
-
-import com.google.android.collect.Lists;
-
-import org.junit.Test;
-
-import java.util.List;
-
-public class AbstractResolverComparatorTest {
-
- @Test
- public void testPinned() {
- ResolvedComponentInfo r1 = createResolvedComponentInfo(
- new ComponentName("package", "class"));
- r1.setPinned(true);
-
- ResolvedComponentInfo r2 = createResolvedComponentInfo(
- new ComponentName("zackage", "zlass"));
-
- Context context = InstrumentationRegistry.getTargetContext();
- AbstractResolverComparator comparator = getTestComparator(context, null);
-
- assertEquals("Pinned ranks over unpinned", -1, comparator.compare(r1, r2));
- assertEquals("Unpinned ranks under pinned", 1, comparator.compare(r2, r1));
- }
-
- @Test
- public void testBothPinned() {
- ResolvedComponentInfo r1 = createResolvedComponentInfo(
- new ComponentName("package", "class"));
- r1.setPinned(true);
-
- ResolvedComponentInfo r2 = createResolvedComponentInfo(
- new ComponentName("zackage", "zlass"));
- r2.setPinned(true);
-
- Context context = InstrumentationRegistry.getTargetContext();
- AbstractResolverComparator comparator = getTestComparator(context, null);
-
- assertEquals("Both pinned should rank alphabetically", -1, comparator.compare(r1, r2));
- }
-
- @Test
- public void testPromoteToFirst() {
- ComponentName promoteToFirst = new ComponentName("promoted-package", "class");
- ResolvedComponentInfo r1 = createResolvedComponentInfo(promoteToFirst);
-
- ResolvedComponentInfo r2 = createResolvedComponentInfo(
- new ComponentName("package", "class"));
-
- Context context = InstrumentationRegistry.getTargetContext();
- AbstractResolverComparator comparator = getTestComparator(context, promoteToFirst);
-
- assertEquals("PromoteToFirst ranks over non-cemented", -1, comparator.compare(r1, r2));
- assertEquals("Non-cemented ranks under PromoteToFirst", 1, comparator.compare(r2, r1));
- }
-
- @Test
- public void testPromoteToFirstOverPinned() {
- ComponentName cementedComponent = new ComponentName("promoted-package", "class");
- ResolvedComponentInfo r1 = createResolvedComponentInfo(cementedComponent);
-
- ResolvedComponentInfo r2 = createResolvedComponentInfo(
- new ComponentName("package", "class"));
- r2.setPinned(true);
-
- Context context = InstrumentationRegistry.getTargetContext();
- AbstractResolverComparator comparator = getTestComparator(context, cementedComponent);
-
- assertEquals("PromoteToFirst ranks over pinned", -1, comparator.compare(r1, r2));
- assertEquals("Pinned ranks under PromoteToFirst", 1, comparator.compare(r2, r1));
- }
-
- private ResolvedComponentInfo createResolvedComponentInfo(ComponentName component) {
- ResolveInfo info = new ResolveInfo();
- info.activityInfo = new ActivityInfo();
- info.activityInfo.packageName = component.getPackageName();
- info.activityInfo.name = component.getClassName();
- return new ResolvedComponentInfo(component, new Intent(), info);
- }
-
- private AbstractResolverComparator getTestComparator(
- Context context, ComponentName promoteToFirst) {
- Intent intent = new Intent();
-
- AbstractResolverComparator testComparator =
- new AbstractResolverComparator(context, intent,
- Lists.newArrayList(context.getUser()), promoteToFirst) {
-
- @Override
- public int compare(ResolveInfo lhs, ResolveInfo rhs) {
- // Used for testing pinning, so we should never get here --- the overrides
- // should determine the result instead.
- return 1;
- }
-
- @Override
- public void doCompute(List<ResolvedComponentInfo> targets) {}
-
- @Override
- public float getScore(TargetInfo targetInfo) {
- return 0;
- }
-
- @Override
- public void handleResultMessage(Message message) {}
- };
- return testComparator;
- }
-
-}
diff --git a/java/tests/src/com/android/intentresolver/shortcuts/ScopedAppTargetListCallbackTest.kt b/java/tests/src/com/android/intentresolver/shortcuts/ScopedAppTargetListCallbackTest.kt
deleted file mode 100644
index c81e88ab..00000000
--- a/java/tests/src/com/android/intentresolver/shortcuts/ScopedAppTargetListCallbackTest.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.shortcuts
-
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import org.junit.Test
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class ScopedAppTargetListCallbackTest {
-
- @Test
- fun test_consumerInvocations_onlyInvokedWhileScopeIsActive() {
- val scope = TestScope(UnconfinedTestDispatcher())
- var counter = 0
- val testSubject = ScopedAppTargetListCallback(scope) { counter++ }.toConsumer()
-
- testSubject.accept(ArrayList())
-
- assertThat(counter).isEqualTo(1)
-
- scope.cancel()
- testSubject.accept(ArrayList())
-
- assertThat(counter).isEqualTo(1)
- }
-
- @Test
- fun test_appPredictorCallbackInvocations_onlyInvokedWhileScopeIsActive() {
- val scope = TestScope(UnconfinedTestDispatcher())
- var counter = 0
- val testSubject = ScopedAppTargetListCallback(scope) { counter++ }.toAppPredictorCallback()
-
- testSubject.onTargetsAvailable(ArrayList())
-
- assertThat(counter).isEqualTo(1)
-
- scope.cancel()
- testSubject.onTargetsAvailable(ArrayList())
-
- assertThat(counter).isEqualTo(1)
- }
-
- @Test
- fun test_createdWithClosedScope_noCallbackInvocations() {
- val scope = TestScope(UnconfinedTestDispatcher()).apply { cancel() }
- var counter = 0
- val testSubject = ScopedAppTargetListCallback(scope) { counter++ }.toConsumer()
-
- testSubject.accept(ArrayList())
-
- assertThat(counter).isEqualTo(0)
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt b/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt
deleted file mode 100644
index 43d0df79..00000000
--- a/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt
+++ /dev/null
@@ -1,490 +0,0 @@
-/*
- * 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.shortcuts
-
-import android.app.prediction.AppPredictor
-import android.content.ComponentName
-import android.content.Context
-import android.content.IntentFilter
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ApplicationInfoFlags
-import android.content.pm.ShortcutManager
-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
-import com.android.intentresolver.createShortcutInfo
-import com.android.intentresolver.mock
-import com.android.intentresolver.whenever
-import java.util.function.Consumer
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestCoroutineScheduler
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertArrayEquals
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Test
-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
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-class ShortcutLoaderTest {
- private val appInfo =
- ApplicationInfo().apply {
- enabled = true
- flags = 0
- }
- private val pm =
- mock<PackageManager> {
- whenever(getApplicationInfo(any(), any<ApplicationInfoFlags>())).thenReturn(appInfo)
- }
- private 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 scheduler = TestCoroutineScheduler()
- private val dispatcher = UnconfinedTestDispatcher(scheduler)
- private val scope = TestScope(dispatcher)
- private val intentFilter = mock<IntentFilter>()
- private val appPredictor = mock<ShortcutLoader.AppPredictorProxy>()
- private val callback = mock<Consumer<ShortcutLoader.Result>>()
- private val componentName = ComponentName("pkg", "Class")
- private val appTarget =
- mock<DisplayResolveInfo> { whenever(resolvedComponentName).thenReturn(componentName) }
- private val appTargets = arrayOf(appTarget)
- private val matchingShortcutInfo = createShortcutInfo("id-0", componentName, 1)
-
- @Test
- fun test_loadShortcutsWithAppPredictor_resultIntegrity() =
- scope.runTest {
- val testSubject =
- ShortcutLoader(
- context,
- backgroundScope,
- appPredictor,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
-
- testSubject.updateAppTargets(appTargets)
-
- val matchingAppTarget = createAppTarget(matchingShortcutInfo)
- val shortcuts =
- listOf(
- matchingAppTarget,
- // an AppTarget that does not belong to any resolved application; should be
- // ignored
- createAppTarget(
- createShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
- )
- )
- val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>()
- verify(appPredictor, atLeastOnce())
- .registerPredictionUpdates(any(), capture(appPredictorCallbackCaptor))
- appPredictorCallbackCaptor.value.onTargetsAvailable(shortcuts)
-
- 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)
- assertArrayEquals(
- "Wrong input app targets in the result",
- appTargets,
- result.appTargets
- )
- assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
- assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
- for (shortcut in result.shortcutsByApp[0].shortcuts) {
- assertEquals(
- "Wrong AppTarget in the cache",
- matchingAppTarget,
- result.directShareAppTargetCache[shortcut]
- )
- assertEquals(
- "Wrong ShortcutInfo in the cache",
- matchingShortcutInfo,
- result.directShareShortcutInfoCache[shortcut]
- )
- }
- }
-
- @Test
- fun test_loadShortcutsWithShortcutManager_resultIntegrity() =
- scope.runTest {
- val shortcutManagerResult =
- listOf(
- ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
- // mismatching shortcut
- createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
- )
- val shortcutManager =
- mock<ShortcutManager> {
- whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
- }
- whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
- val testSubject =
- ShortcutLoader(
- context,
- backgroundScope,
- null,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
-
- testSubject.updateAppTargets(appTargets)
-
- val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
- verify(callback, times(1)).accept(capture(resultCaptor))
-
- val result = resultCaptor.value
- assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor)
- assertArrayEquals(
- "Wrong input app targets in the result",
- appTargets,
- result.appTargets
- )
- assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
- assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
- for (shortcut in result.shortcutsByApp[0].shortcuts) {
- assertTrue(
- "AppTargets are not expected the cache of a ShortcutManager result",
- result.directShareAppTargetCache.isEmpty()
- )
- assertEquals(
- "Wrong ShortcutInfo in the cache",
- matchingShortcutInfo,
- result.directShareShortcutInfoCache[shortcut]
- )
- }
- }
-
- @Test
- fun test_appPredictorReturnsEmptyList_fallbackToShortcutManager() =
- scope.runTest {
- val shortcutManagerResult =
- listOf(
- ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
- // mismatching shortcut
- createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
- )
- val shortcutManager =
- mock<ShortcutManager> {
- whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
- }
- whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
- val testSubject =
- ShortcutLoader(
- context,
- backgroundScope,
- appPredictor,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
-
- testSubject.updateAppTargets(appTargets)
-
- verify(appPredictor, times(1)).requestPredictionUpdate()
- val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>()
- verify(appPredictor, times(1))
- .registerPredictionUpdates(any(), capture(appPredictorCallbackCaptor))
- appPredictorCallbackCaptor.value.onTargetsAvailable(emptyList())
-
- val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
- verify(callback, times(1)).accept(capture(resultCaptor))
-
- val result = resultCaptor.value
- assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor)
- assertArrayEquals(
- "Wrong input app targets in the result",
- appTargets,
- result.appTargets
- )
- assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
- assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
- for (shortcut in result.shortcutsByApp[0].shortcuts) {
- assertTrue(
- "AppTargets are not expected the cache of a ShortcutManager result",
- result.directShareAppTargetCache.isEmpty()
- )
- assertEquals(
- "Wrong ShortcutInfo in the cache",
- matchingShortcutInfo,
- result.directShareShortcutInfoCache[shortcut]
- )
- }
- }
-
- @Test
- fun test_appPredictor_requestPredictionUpdateFailure_fallbackToShortcutManager() =
- scope.runTest {
- val shortcutManagerResult =
- listOf(
- ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
- // mismatching shortcut
- createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
- )
- val shortcutManager =
- mock<ShortcutManager> {
- whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
- }
- whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
- whenever(appPredictor.requestPredictionUpdate())
- .thenThrow(IllegalStateException("Test exception"))
- val testSubject =
- ShortcutLoader(
- context,
- backgroundScope,
- appPredictor,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
-
- testSubject.updateAppTargets(appTargets)
-
- verify(appPredictor, times(1)).requestPredictionUpdate()
-
- val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
- verify(callback, times(1)).accept(capture(resultCaptor))
-
- val result = resultCaptor.value
- assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor)
- assertArrayEquals(
- "Wrong input app targets in the result",
- appTargets,
- result.appTargets
- )
- assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
- assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
- for (shortcut in result.shortcutsByApp[0].shortcuts) {
- assertTrue(
- "AppTargets are not expected the cache of a ShortcutManager result",
- result.directShareAppTargetCache.isEmpty()
- )
- assertEquals(
- "Wrong ShortcutInfo in the cache",
- matchingShortcutInfo,
- result.directShareShortcutInfoCache[shortcut]
- )
- }
- }
-
- @Test
- fun test_ShortcutLoader_shortcutsRequestedIndependentlyFromAppTargets() =
- scope.runTest {
- ShortcutLoader(
- context,
- backgroundScope,
- appPredictor,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
-
- verify(appPredictor, times(1)).requestPredictionUpdate()
- verify(callback, never()).accept(any())
- }
-
- @Test
- fun test_ShortcutLoader_noResultsWithoutAppTargets() =
- scope.runTest {
- val shortcutManagerResult =
- listOf(
- ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
- // mismatching shortcut
- createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
- )
- val shortcutManager =
- mock<ShortcutManager> {
- whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
- }
- whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
- val testSubject =
- ShortcutLoader(
- context,
- backgroundScope,
- null,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
-
- verify(shortcutManager, times(1)).getShareTargets(any())
- verify(callback, never()).accept(any())
-
- testSubject.reset()
-
- verify(shortcutManager, times(2)).getShareTargets(any())
- verify(callback, never()).accept(any())
-
- testSubject.updateAppTargets(appTargets)
-
- verify(shortcutManager, times(2)).getShareTargets(any())
- verify(callback, times(1)).accept(any())
- }
-
- @Test
- fun test_OnScopeCancellation_unsubscribeFromAppPredictor() {
- scope.runTest {
- ShortcutLoader(
- context,
- backgroundScope,
- appPredictor,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
-
- verify(appPredictor, never()).unregisterPredictionUpdates(any())
- }
-
- verify(appPredictor, times(1)).unregisterPredictionUpdates(any())
- }
-
- @Test
- fun test_workProfileNotRunning_doNotCallServices() {
- testDisabledWorkProfileDoNotCallSystem(isUserRunning = false)
- }
-
- @Test
- fun test_workProfileLocked_doNotCallServices() {
- testDisabledWorkProfileDoNotCallSystem(isUserUnlocked = false)
- }
-
- @Test
- fun test_workProfileQuiteModeEnabled_doNotCallServices() {
- testDisabledWorkProfileDoNotCallSystem(isQuietModeEnabled = true)
- }
-
- @Test
- fun test_mainProfileNotRunning_callServicesAnyway() {
- testAlwaysCallSystemForMainProfile(isUserRunning = false)
- }
-
- @Test
- fun test_mainProfileLocked_callServicesAnyway() {
- testAlwaysCallSystemForMainProfile(isUserUnlocked = false)
- }
-
- @Test
- fun test_mainProfileQuiteModeEnabled_callServicesAnyway() {
- testAlwaysCallSystemForMainProfile(isQuietModeEnabled = true)
- }
-
- private fun testDisabledWorkProfileDoNotCallSystem(
- isUserRunning: Boolean = true,
- isUserUnlocked: Boolean = true,
- isQuietModeEnabled: Boolean = false
- ) =
- scope.runTest {
- val userHandle = UserHandle.of(10)
- with(userManager) {
- whenever(isUserRunning(userHandle)).thenReturn(isUserRunning)
- whenever(isUserUnlocked(userHandle)).thenReturn(isUserUnlocked)
- whenever(isQuietModeEnabled(userHandle)).thenReturn(isQuietModeEnabled)
- }
- whenever(context.getSystemService(Context.USER_SERVICE)).thenReturn(userManager)
- val appPredictor = mock<ShortcutLoader.AppPredictorProxy>()
- val callback = mock<Consumer<ShortcutLoader.Result>>()
- val testSubject =
- ShortcutLoader(
- context,
- backgroundScope,
- appPredictor,
- userHandle,
- false,
- intentFilter,
- dispatcher,
- callback
- )
-
- testSubject.updateAppTargets(arrayOf<DisplayResolveInfo>(mock()))
-
- verify(appPredictor, never()).requestPredictionUpdate()
- }
-
- private fun testAlwaysCallSystemForMainProfile(
- isUserRunning: Boolean = true,
- isUserUnlocked: Boolean = true,
- isQuietModeEnabled: Boolean = false
- ) =
- scope.runTest {
- val userHandle = UserHandle.of(10)
- with(userManager) {
- whenever(isUserRunning(userHandle)).thenReturn(isUserRunning)
- whenever(isUserUnlocked(userHandle)).thenReturn(isUserUnlocked)
- whenever(isQuietModeEnabled(userHandle)).thenReturn(isQuietModeEnabled)
- }
- whenever(context.getSystemService(Context.USER_SERVICE)).thenReturn(userManager)
- val appPredictor = mock<ShortcutLoader.AppPredictorProxy>()
- val callback = mock<Consumer<ShortcutLoader.Result>>()
- val testSubject =
- ShortcutLoader(
- context,
- backgroundScope,
- appPredictor,
- userHandle,
- true,
- intentFilter,
- dispatcher,
- callback
- )
-
- testSubject.updateAppTargets(arrayOf<DisplayResolveInfo>(mock()))
-
- verify(appPredictor, times(1)).requestPredictionUpdate()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/shortcuts/ShortcutToChooserTargetConverterTest.kt b/java/tests/src/com/android/intentresolver/shortcuts/ShortcutToChooserTargetConverterTest.kt
deleted file mode 100644
index e0de005d..00000000
--- a/java/tests/src/com/android/intentresolver/shortcuts/ShortcutToChooserTargetConverterTest.kt
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * 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.shortcuts
-
-import android.app.prediction.AppTarget
-import android.content.ComponentName
-import android.content.Intent
-import android.content.pm.ShortcutInfo
-import android.content.pm.ShortcutManager.ShareShortcutInfo
-import android.service.chooser.ChooserTarget
-import com.android.intentresolver.createAppTarget
-import com.android.intentresolver.createShareShortcutInfo
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNotNull
-import org.junit.Test
-
-private const val PACKAGE = "org.package"
-
-class ShortcutToChooserTargetConverterTest {
- private val testSubject = ShortcutToChooserTargetConverter()
- private val ranks = arrayOf(3 ,7, 1 ,3)
- private val shortcuts = ranks
- .foldIndexed(ArrayList<ShareShortcutInfo>(ranks.size)) { i, acc, rank ->
- val id = i + 1
- acc.add(
- createShareShortcutInfo(
- id = "id-$i",
- componentName = ComponentName(PACKAGE, "Class$id"),
- rank,
- )
- )
- acc
- }
-
- @Test
- fun testConvertToChooserTarget_predictionService() {
- val appTargets = shortcuts.map { createAppTarget(it.shortcutInfo) }
- val expectedOrderAllShortcuts = intArrayOf(0, 1, 2, 3)
- val expectedScoreAllShortcuts = floatArrayOf(1.0f, 0.99f, 0.98f, 0.97f)
- val appTargetCache = HashMap<ChooserTarget, AppTarget>()
- val shortcutInfoCache = HashMap<ChooserTarget, ShortcutInfo>()
-
- var chooserTargets = testSubject.convertToChooserTarget(
- shortcuts,
- shortcuts,
- appTargets,
- appTargetCache,
- shortcutInfoCache,
- )
-
- assertCorrectShortcutToChooserTargetConversion(
- shortcuts,
- chooserTargets,
- expectedOrderAllShortcuts,
- expectedScoreAllShortcuts,
- )
- assertAppTargetCache(chooserTargets, appTargetCache)
- assertShortcutInfoCache(chooserTargets, shortcutInfoCache)
-
- val subset = shortcuts.subList(1, shortcuts.size)
- val expectedOrderSubset = intArrayOf(1, 2, 3)
- val expectedScoreSubset = floatArrayOf(0.99f, 0.98f, 0.97f)
- appTargetCache.clear()
- shortcutInfoCache.clear()
-
- chooserTargets = testSubject.convertToChooserTarget(
- subset,
- shortcuts,
- appTargets,
- appTargetCache,
- shortcutInfoCache,
- )
-
- assertCorrectShortcutToChooserTargetConversion(
- shortcuts,
- chooserTargets,
- expectedOrderSubset,
- expectedScoreSubset,
- )
- assertAppTargetCache(chooserTargets, appTargetCache)
- assertShortcutInfoCache(chooserTargets, shortcutInfoCache)
- }
-
- @Test
- fun testConvertToChooserTarget_shortcutManager() {
- val testSubject = ShortcutToChooserTargetConverter()
- val expectedOrderAllShortcuts = intArrayOf(2, 0, 3, 1)
- val expectedScoreAllShortcuts = floatArrayOf(1.0f, 0.99f, 0.99f, 0.98f)
- val shortcutInfoCache = HashMap<ChooserTarget, ShortcutInfo>()
-
- var chooserTargets = testSubject.convertToChooserTarget(
- shortcuts,
- shortcuts,
- null,
- null,
- shortcutInfoCache,
- )
-
- assertCorrectShortcutToChooserTargetConversion(
- shortcuts, chooserTargets,
- expectedOrderAllShortcuts, expectedScoreAllShortcuts
- )
- assertShortcutInfoCache(chooserTargets, shortcutInfoCache)
-
- val subset: MutableList<ShareShortcutInfo> = java.util.ArrayList()
- subset.add(shortcuts[1])
- subset.add(shortcuts[2])
- subset.add(shortcuts[3])
- val expectedOrderSubset = intArrayOf(2, 3, 1)
- val expectedScoreSubset = floatArrayOf(1.0f, 0.99f, 0.98f)
- shortcutInfoCache.clear()
-
- chooserTargets = testSubject.convertToChooserTarget(
- subset,
- shortcuts,
- null,
- null,
- shortcutInfoCache,
- )
-
- assertCorrectShortcutToChooserTargetConversion(
- shortcuts, chooserTargets,
- expectedOrderSubset, expectedScoreSubset
- )
- assertShortcutInfoCache(chooserTargets, shortcutInfoCache)
- }
-
- private fun assertCorrectShortcutToChooserTargetConversion(
- shortcuts: List<ShareShortcutInfo>,
- chooserTargets: List<ChooserTarget>,
- expectedOrder: IntArray,
- expectedScores: FloatArray,
- ) {
- assertEquals("Unexpected ChooserTarget count", expectedOrder.size, chooserTargets.size)
- for (i in chooserTargets.indices) {
- val ct = chooserTargets[i]
- val si = shortcuts[expectedOrder[i]].shortcutInfo
- val cn = shortcuts[expectedOrder[i]].targetComponent
- assertEquals(si.id, ct.intentExtras.getString(Intent.EXTRA_SHORTCUT_ID))
- assertEquals(si.label, ct.title)
- assertEquals(expectedScores[i], ct.score)
- assertEquals(cn, ct.componentName)
- }
- }
-
- private fun assertAppTargetCache(
- chooserTargets: List<ChooserTarget>, cache: Map<ChooserTarget, AppTarget>
- ) {
- for (ct in chooserTargets) {
- val target = cache[ct]
- assertNotNull("AppTarget is missing", target)
- }
- }
-
- private fun assertShortcutInfoCache(
- chooserTargets: List<ChooserTarget>, cache: Map<ChooserTarget, ShortcutInfo>
- ) {
- for (ct in chooserTargets) {
- val si = cache[ct]
- assertNotNull("AppTarget is missing", si)
- }
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/util/TestExecutor.kt b/java/tests/src/com/android/intentresolver/util/TestExecutor.kt
deleted file mode 100644
index 214b9707..00000000
--- a/java/tests/src/com/android/intentresolver/util/TestExecutor.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.util
-
-import java.util.concurrent.Executor
-
-class TestExecutor(private val immediate: Boolean = false) : Executor {
- private var pendingCommands = ArrayDeque<Runnable>()
-
- val pendingCommandCount: Int
- get() = pendingCommands.size
-
- override fun execute(command: Runnable) {
- if (immediate) {
- command.run()
- } else {
- pendingCommands.add(command)
- }
- }
-
- fun runUntilIdle() {
- while (pendingCommands.isNotEmpty()) {
- pendingCommands.removeFirst().run()
- }
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/util/UriFiltersTest.kt b/java/tests/src/com/android/intentresolver/util/UriFiltersTest.kt
deleted file mode 100644
index 18218064..00000000
--- a/java/tests/src/com/android/intentresolver/util/UriFiltersTest.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.android.intentresolver.util
-
-import android.app.PendingIntent
-import android.content.IIntentReceiver
-import android.content.IIntentSender
-import android.content.Intent
-import android.graphics.Bitmap
-import android.graphics.drawable.Icon
-import android.net.Uri
-import android.os.Binder
-import android.os.Bundle
-import android.os.IBinder
-import android.os.UserHandle
-import android.service.chooser.ChooserAction
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class UriFiltersTest {
-
- @Test
- fun uri_ownedByCurrentUser_noUserId() {
- val uri = Uri.parse("content://media/images/12345")
- assertTrue("Uri without userId should always return true", uri.ownedByCurrentUser)
- }
-
- @Test
- fun uri_ownedByCurrentUser_selfUserId() {
- val uri = Uri.parse("content://${UserHandle.myUserId()}@media/images/12345")
- assertTrue("Uri with own userId should return true", uri.ownedByCurrentUser)
- }
-
- @Test
- fun uri_ownedByCurrentUser_otherUserId() {
- val otherUserId = UserHandle.myUserId() + 10
- val uri = Uri.parse("content://${otherUserId}@media/images/12345")
- assertFalse("Uri with other userId should return false", uri.ownedByCurrentUser)
- }
-
- @Test
- fun chooserAction_hasValidIcon_bitmap() =
- smallBitmap().use {
- val icon = Icon.createWithBitmap(it)
- val action = actionWithIcon(icon)
- assertTrue("No uri, assumed valid", hasValidIcon(action))
- }
-
- @Test
- fun chooserAction_hasValidIcon_uri() {
- val icon = Icon.createWithContentUri("content://provider/content/12345")
- assertTrue("No userId in uri, uri is valid", hasValidIcon(actionWithIcon(icon)))
- }
- @Test
- fun chooserAction_hasValidIcon_uri_unowned() {
- val userId = UserHandle.myUserId() + 10
- val icon = Icon.createWithContentUri("content://${userId}@provider/content/12345")
- assertFalse("uri userId references a different user", hasValidIcon(actionWithIcon(icon)))
- }
-
- private fun smallBitmap() = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
-
- private fun mockAction(): PendingIntent {
- return PendingIntent(
- object : IIntentSender {
- override fun asBinder(): IBinder = Binder()
- override fun send(
- code: Int,
- intent: Intent?,
- resolvedType: String?,
- whitelistToken: IBinder?,
- finishedReceiver: IIntentReceiver?,
- requiredPermission: String?,
- options: Bundle?
- ) {
- /* empty */
- }
- }
- )
- }
-
- private fun actionWithIcon(icon: Icon): ChooserAction {
- return ChooserAction.Builder(icon, "", mockAction()).build()
- }
-
- /** Unconditionally recycles the [Bitmap] after running the given block */
- private fun Bitmap.use(block: (Bitmap) -> Unit) =
- try {
- block(this)
- } finally {
- recycle()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/ChooserActionFactoryTest.kt b/java/tests/src/com/android/intentresolver/v2/ChooserActionFactoryTest.kt
deleted file mode 100644
index a1a9bc92..00000000
--- a/java/tests/src/com/android/intentresolver/v2/ChooserActionFactoryTest.kt
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2
-
-import android.app.Activity
-import android.app.PendingIntent
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Context.RECEIVER_EXPORTED
-import android.content.Intent
-import android.content.IntentFilter
-import android.content.res.Resources
-import android.graphics.drawable.Icon
-import android.service.chooser.ChooserAction
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.intentresolver.ChooserRequestParameters
-import com.android.intentresolver.logging.EventLog
-import com.android.intentresolver.mock
-import com.android.intentresolver.whenever
-import com.google.common.collect.ImmutableList
-import com.google.common.truth.Truth.assertThat
-import java.util.Optional
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-import java.util.function.Consumer
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito
-
-@RunWith(AndroidJUnit4::class)
-class ChooserActionFactoryTest {
- private val context = InstrumentationRegistry.getInstrumentation().context
-
- private val logger = mock<EventLog>()
- private val actionLabel = "Action label"
- private val modifyShareLabel = "Modify share"
- 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 val resultConsumer =
- object : Consumer<Int> {
- var latestReturn = Integer.MIN_VALUE
-
- override fun accept(resultCode: Int) {
- latestReturn = resultCode
- }
- }
-
- @Before
- fun setup() {
- context.registerReceiver(testReceiver, IntentFilter(testAction), RECEIVER_EXPORTED)
- }
-
- @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 pending intent has been called
- assertTrue("Timed out waiting for broadcast", countdown.await(2500, TimeUnit.MILLISECONDS))
- }
-
- @Test
- fun testNoModifyShareAction() {
- val factory = createFactory(includeModifyShare = false)
-
- assertThat(factory.modifyShareAction).isNull()
- }
-
- @Test
- fun testModifyShareAction() {
- val factory = createFactory(includeModifyShare = true)
-
- val action = factory.modifyShareAction ?: error("Modify share action should not be null")
- action.onClicked.run()
-
- Mockito.verify(logger).logActionSelected(eq(EventLog.SELECTION_TYPE_MODIFY_SHARE))
- assertEquals(Activity.RESULT_OK, resultConsumer.latestReturn)
- // Verify the pending intent has been called
- assertTrue("Timed out waiting for broadcast", countdown.await(2500, TimeUnit.MILLISECONDS))
- }
-
- @Test
- fun nonSendAction_noCopyRunnable() {
- val targetIntent =
- Intent(Intent.ACTION_SEND_MULTIPLE).apply {
- putExtra(Intent.EXTRA_TEXT, "Text to show")
- }
-
- val chooserRequest =
- mock<ChooserRequestParameters> {
- whenever(this.targetIntent).thenReturn(targetIntent)
- whenever(chooserActions).thenReturn(ImmutableList.of())
- }
- val testSubject =
- ChooserActionFactory(
- context,
- chooserRequest,
- Optional.empty(),
- logger,
- {},
- { null },
- mock(),
- {},
- )
- assertThat(testSubject.copyButtonRunnable).isNull()
- }
-
- @Test
- fun sendActionNoText_noCopyRunnable() {
- val targetIntent = Intent(Intent.ACTION_SEND)
-
- val chooserRequest =
- mock<ChooserRequestParameters> {
- whenever(this.targetIntent).thenReturn(targetIntent)
- whenever(chooserActions).thenReturn(ImmutableList.of())
- }
- val testSubject =
- ChooserActionFactory(
- context,
- chooserRequest,
- Optional.empty(),
- logger,
- {},
- { null },
- mock(),
- {},
- )
- assertThat(testSubject.copyButtonRunnable).isNull()
- }
-
- @Test
- fun sendActionWithText_nonNullCopyRunnable() {
- val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_TEXT, "Text") }
-
- val chooserRequest =
- mock<ChooserRequestParameters> {
- whenever(this.targetIntent).thenReturn(targetIntent)
- whenever(chooserActions).thenReturn(ImmutableList.of())
- }
- val testSubject =
- ChooserActionFactory(
- context,
- chooserRequest,
- Optional.empty(),
- logger,
- {},
- { null },
- mock(),
- {},
- )
- assertThat(testSubject.copyButtonRunnable).isNotNull()
- }
-
- private fun createFactory(includeModifyShare: Boolean = false): ChooserActionFactory {
- val testPendingIntent =
- PendingIntent.getBroadcast(context, 0, Intent(testAction), PendingIntent.FLAG_IMMUTABLE)
- 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) {
- val modifyShare =
- ChooserAction.Builder(
- Icon.createWithResource("", Resources.ID_NULL),
- modifyShareLabel,
- testPendingIntent
- )
- .build()
- whenever(chooserRequest.modifyShareAction).thenReturn(modifyShare)
- }
-
- return ChooserActionFactory(
- context,
- chooserRequest,
- Optional.empty(),
- logger,
- {},
- { null },
- mock(),
- resultConsumer
- )
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/ChooserActivityOverrideData.java b/java/tests/src/com/android/intentresolver/v2/ChooserActivityOverrideData.java
deleted file mode 100644
index 32eabbed..00000000
--- a/java/tests/src/com/android/intentresolver/v2/ChooserActivityOverrideData.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2021 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.v2;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.os.UserHandle;
-
-import com.android.intentresolver.AnnotatedUserHandles;
-import com.android.intentresolver.WorkProfileAvailabilityManager;
-import com.android.intentresolver.chooser.TargetInfo;
-import com.android.intentresolver.contentpreview.ImageLoader;
-import com.android.intentresolver.emptystate.CrossProfileIntentsChecker;
-import com.android.intentresolver.shortcuts.ShortcutLoader;
-
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-import kotlin.jvm.functions.Function2;
-
-/**
- * Singleton providing overrides to be applied by any {@code IChooserWrapper} used in testing.
- * We cannot directly mock the activity created since instrumentation creates it, so instead we use
- * this singleton to modify behavior.
- */
-public class ChooserActivityOverrideData {
- private static ChooserActivityOverrideData sInstance = null;
-
- public static ChooserActivityOverrideData getInstance() {
- if (sInstance == null) {
- sInstance = new ChooserActivityOverrideData();
- }
- return sInstance;
- }
-
- @SuppressWarnings("Since15")
- public Function<PackageManager, PackageManager> createPackageManager;
- public Function<TargetInfo, Boolean> onSafelyStartInternalCallback;
- public Function<TargetInfo, Boolean> onSafelyStartCallback;
- public Function2<UserHandle, Consumer<ShortcutLoader.Result>, ShortcutLoader>
- shortcutLoaderFactory = (userHandle, callback) -> null;
- public ChooserActivity.ChooserListController resolverListController;
- public ChooserActivity.ChooserListController workResolverListController;
- public Boolean isVoiceInteraction;
- public Cursor resolverCursor;
- public boolean resolverForceException;
- public ImageLoader imageLoader;
- public int alternateProfileSetting;
- public Resources resources;
- public AnnotatedUserHandles annotatedUserHandles;
- public boolean hasCrossProfileIntents;
- public boolean isQuietModeEnabled;
- public Integer myUserId;
- public WorkProfileAvailabilityManager mWorkProfileAvailability;
- public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
- public PackageManager packageManager;
-
- public void reset() {
- onSafelyStartInternalCallback = null;
- isVoiceInteraction = null;
- createPackageManager = null;
- imageLoader = null;
- resolverCursor = null;
- resolverForceException = false;
- resolverListController = mock(ChooserActivity.ChooserListController.class);
- workResolverListController = mock(ChooserActivity.ChooserListController.class);
- alternateProfileSetting = 0;
- resources = null;
- annotatedUserHandles = AnnotatedUserHandles.newBuilder()
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(UserHandle.SYSTEM)
- .setPersonalProfileUserHandle(UserHandle.SYSTEM)
- .build();
- hasCrossProfileIntents = true;
- isQuietModeEnabled = false;
- myUserId = null;
- packageManager = null;
- mWorkProfileAvailability = new WorkProfileAvailabilityManager(null, null, null) {
- @Override
- public boolean isQuietModeEnabled() {
- return isQuietModeEnabled;
- }
-
- @Override
- public boolean isWorkProfileUserUnlocked() {
- return true;
- }
-
- @Override
- public void requestQuietModeEnabled(boolean enabled) {
- isQuietModeEnabled = enabled;
- }
-
- @Override
- public void markWorkProfileEnabledBroadcastReceived() {}
-
- @Override
- public boolean isWaitingToEnableWorkProfile() {
- return false;
- }
- };
- shortcutLoaderFactory = ((userHandle, resultConsumer) -> null);
-
- mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class);
- when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt()))
- .thenAnswer(invocation -> hasCrossProfileIntents);
- }
-
- private ChooserActivityOverrideData() {}
-}
-
diff --git a/java/tests/src/com/android/intentresolver/v2/ChooserWrapperActivity.java b/java/tests/src/com/android/intentresolver/v2/ChooserWrapperActivity.java
deleted file mode 100644
index 5572bb24..00000000
--- a/java/tests/src/com/android/intentresolver/v2/ChooserWrapperActivity.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2008 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.v2;
-
-import android.annotation.Nullable;
-import android.app.prediction.AppPredictor;
-import android.app.usage.UsageStatsManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.UserHandle;
-
-import androidx.lifecycle.ViewModelProvider;
-
-import com.android.intentresolver.ChooserListAdapter;
-import com.android.intentresolver.ChooserRequestParameters;
-import com.android.intentresolver.IChooserWrapper;
-import com.android.intentresolver.ResolverListController;
-import com.android.intentresolver.TestContentPreviewViewModel;
-import com.android.intentresolver.chooser.DisplayResolveInfo;
-import com.android.intentresolver.chooser.TargetInfo;
-import com.android.intentresolver.emptystate.CrossProfileIntentsChecker;
-import com.android.intentresolver.grid.ChooserGridAdapter;
-import com.android.intentresolver.icons.TargetDataLoader;
-import com.android.intentresolver.shortcuts.ShortcutLoader;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Simple wrapper around chooser activity to be able to initiate it under test. For more
- * information, see {@code com.android.internal.app.ChooserWrapperActivity}.
- */
-public class ChooserWrapperActivity extends ChooserActivity implements IChooserWrapper {
- static final ChooserActivityOverrideData sOverrides = ChooserActivityOverrideData.getInstance();
- private UsageStatsManager mUsm;
-
- public ChooserWrapperActivity() {
- super();
- mLogic = new TestChooserActivityLogic(
- "ChooserWrapper",
- () -> this,
- this::onWorkProfileStatusUpdated,
- () -> mTargetDataLoader,
- this::onPreinitialization,
- sOverrides
- );
- }
-
- // ResolverActivity (the base class of ChooserActivity) inspects the launched-from UID at
- // onCreate and needs to see some non-negative value in the test.
- @Override
- public int getLaunchedFromUid() {
- return 1234;
- }
-
- @Override
- public ChooserListAdapter createChooserListAdapter(
- Context context,
- List<Intent> payloadIntents,
- Intent[] initialIntents,
- List<ResolveInfo> rList,
- boolean filterLastUsed,
- ResolverListController resolverListController,
- UserHandle userHandle,
- Intent targetIntent,
- ChooserRequestParameters chooserRequest,
- int maxTargetsPerRow,
- TargetDataLoader targetDataLoader) {
- PackageManager packageManager =
- sOverrides.packageManager == null ? context.getPackageManager()
- : sOverrides.packageManager;
- return new ChooserListAdapter(
- context,
- payloadIntents,
- initialIntents,
- rList,
- filterLastUsed,
- createListController(userHandle),
- userHandle,
- targetIntent,
- this,
- packageManager,
- getEventLog(),
- chooserRequest,
- maxTargetsPerRow,
- userHandle,
- targetDataLoader);
- }
-
- @Override
- public ChooserListAdapter getAdapter() {
- return mChooserMultiProfilePagerAdapter.getActiveListAdapter();
- }
-
- @Override
- public ChooserListAdapter getPersonalListAdapter() {
- return ((ChooserGridAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(0))
- .getListAdapter();
- }
-
- @Override
- public ChooserListAdapter getWorkListAdapter() {
- if (mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
- return null;
- }
- return ((ChooserGridAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(1))
- .getListAdapter();
- }
-
- @Override
- public boolean getIsSelected() {
- return mIsSuccessfullySelected;
- }
-
- @Override
- public UsageStatsManager getUsageStatsManager() {
- if (mUsm == null) {
- mUsm = getSystemService(UsageStatsManager.class);
- }
- return mUsm;
- }
-
- @Override
- public boolean isVoiceInteraction() {
- if (sOverrides.isVoiceInteraction != null) {
- return sOverrides.isVoiceInteraction;
- }
- return super.isVoiceInteraction();
- }
-
- @Override
- protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
- if (sOverrides.mCrossProfileIntentsChecker != null) {
- return sOverrides.mCrossProfileIntentsChecker;
- }
- return super.createCrossProfileIntentsChecker();
- }
-
- @Override
- public void safelyStartActivityInternal(TargetInfo cti, UserHandle user,
- @Nullable Bundle options) {
- if (sOverrides.onSafelyStartInternalCallback != null
- && sOverrides.onSafelyStartInternalCallback.apply(cti)) {
- return;
- }
- super.safelyStartActivityInternal(cti, user, options);
- }
-
- @Override
- protected ChooserListController createListController(UserHandle userHandle) {
- if (userHandle == UserHandle.SYSTEM) {
- return sOverrides.resolverListController;
- }
- return sOverrides.workResolverListController;
- }
-
- @Override
- public PackageManager getPackageManager() {
- if (sOverrides.createPackageManager != null) {
- return sOverrides.createPackageManager.apply(super.getPackageManager());
- }
- return super.getPackageManager();
- }
-
- @Override
- public Resources getResources() {
- if (sOverrides.resources != null) {
- return sOverrides.resources;
- }
- return super.getResources();
- }
-
- @Override
- protected ViewModelProvider.Factory createPreviewViewModelFactory() {
- return TestContentPreviewViewModel.Companion.wrap(
- super.createPreviewViewModelFactory(),
- sOverrides.imageLoader);
- }
-
- @Override
- public Cursor queryResolver(ContentResolver resolver, Uri uri) {
- if (sOverrides.resolverCursor != null) {
- return sOverrides.resolverCursor;
- }
-
- if (sOverrides.resolverForceException) {
- throw new SecurityException("Test exception handling");
- }
-
- return super.queryResolver(resolver, uri);
- }
-
- @Override
- protected boolean isWorkProfile() {
- if (sOverrides.alternateProfileSetting != 0) {
- return sOverrides.alternateProfileSetting == MetricsEvent.MANAGED_PROFILE;
- }
- return super.isWorkProfile();
- }
-
- @Override
- public DisplayResolveInfo createTestDisplayResolveInfo(
- Intent originalIntent,
- ResolveInfo pri,
- CharSequence pLabel,
- CharSequence pInfo,
- Intent replacementIntent) {
- return DisplayResolveInfo.newDisplayResolveInfo(
- originalIntent,
- pri,
- pLabel,
- pInfo,
- replacementIntent);
- }
-
- @Override
- public UserHandle getCurrentUserHandle() {
- return mMultiProfilePagerAdapter.getCurrentUserHandle();
- }
-
- @Override
- public Context createContextAsUser(UserHandle user, int flags) {
- // return the current context as a work profile doesn't really exist in these tests
- return this;
- }
-
- @Override
- protected ShortcutLoader createShortcutLoader(
- Context context,
- AppPredictor appPredictor,
- UserHandle userHandle,
- IntentFilter targetIntentFilter,
- Consumer<ShortcutLoader.Result> callback) {
- ShortcutLoader shortcutLoader =
- sOverrides.shortcutLoaderFactory.invoke(userHandle, callback);
- if (shortcutLoader != null) {
- return shortcutLoader;
- }
- return super.createShortcutLoader(
- context, appPredictor, userHandle, targetIntentFilter, callback);
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/MultiProfilePagerAdapterTest.kt b/java/tests/src/com/android/intentresolver/v2/MultiProfilePagerAdapterTest.kt
deleted file mode 100644
index f5dc0935..00000000
--- a/java/tests/src/com/android/intentresolver/v2/MultiProfilePagerAdapterTest.kt
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2
-
-import android.os.UserHandle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ListView
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.intentresolver.MultiProfilePagerAdapter.PROFILE_PERSONAL
-import com.android.intentresolver.MultiProfilePagerAdapter.PROFILE_WORK
-import com.android.intentresolver.R
-import com.android.intentresolver.ResolverListAdapter
-import com.android.intentresolver.emptystate.EmptyStateProvider
-import com.android.intentresolver.mock
-import com.android.intentresolver.whenever
-import com.google.common.collect.ImmutableList
-import com.google.common.truth.Truth.assertThat
-import java.util.Optional
-import java.util.function.Supplier
-import org.junit.Test
-
-class MultiProfilePagerAdapterTest {
- private val PERSONAL_USER_HANDLE = UserHandle.of(10)
- private val WORK_USER_HANDLE = UserHandle.of(20)
-
- private val context = InstrumentationRegistry.getInstrumentation().getContext()
- private val inflater = Supplier {
- LayoutInflater.from(context).inflate(R.layout.resolver_list_per_profile, null, false)
- as ViewGroup
- }
-
- @Test
- fun testSinglePageProfileAdapter() {
- val personalListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter),
- object : EmptyStateProvider {},
- { false },
- PROFILE_PERSONAL,
- null,
- null,
- inflater,
- { Optional.empty() }
- )
- assertThat(pagerAdapter.count).isEqualTo(1)
- assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_PERSONAL)
- assertThat(pagerAdapter.currentUserHandle).isEqualTo(PERSONAL_USER_HANDLE)
- assertThat(pagerAdapter.getAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.inactiveListAdapter).isNull()
- assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.workListAdapter).isNull()
- assertThat(pagerAdapter.itemCount).isEqualTo(1)
- // TODO: consider covering some of the package-private methods (and making them public?).
- // TODO: consider exercising responsibilities as an implementation of a ViewPager adapter.
- }
-
- @Test
- fun testTwoProfilePagerAdapter() {
- val personalListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) }
- val workListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE) }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter, workListAdapter),
- object : EmptyStateProvider {},
- { false },
- PROFILE_PERSONAL,
- WORK_USER_HANDLE, // TODO: why does this test pass even if this is null?
- null,
- inflater,
- { Optional.empty() }
- )
- assertThat(pagerAdapter.count).isEqualTo(2)
- assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_PERSONAL)
- assertThat(pagerAdapter.currentUserHandle).isEqualTo(PERSONAL_USER_HANDLE)
- assertThat(pagerAdapter.getAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.getAdapterForIndex(1)).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.inactiveListAdapter).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.workListAdapter).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.itemCount).isEqualTo(2)
- // TODO: consider covering some of the package-private methods (and making them public?).
- // TODO: consider exercising responsibilities as an implementation of a ViewPager adapter;
- // especially matching profiles to ListViews?
- // TODO: test ProfileSelectedListener (and getters for "current" state) as the selected
- // page changes. Currently there's no API to change the selected page directly; that's
- // only possible through manipulation of the bound ViewPager.
- }
-
- @Test
- fun testTwoProfilePagerAdapter_workIsDefault() {
- val personalListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) }
- val workListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE) }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter, workListAdapter),
- object : EmptyStateProvider {},
- { false },
- PROFILE_WORK, // <-- This test specifically requests we start on work profile.
- WORK_USER_HANDLE, // TODO: why does this test pass even if this is null?
- null,
- inflater,
- { Optional.empty() }
- )
- assertThat(pagerAdapter.count).isEqualTo(2)
- assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_WORK)
- assertThat(pagerAdapter.currentUserHandle).isEqualTo(WORK_USER_HANDLE)
- assertThat(pagerAdapter.getAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.getAdapterForIndex(1)).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.inactiveListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
- assertThat(pagerAdapter.workListAdapter).isSameInstanceAs(workListAdapter)
- assertThat(pagerAdapter.itemCount).isEqualTo(2)
- // TODO: consider covering some of the package-private methods (and making them public?).
- // TODO: test ProfileSelectedListener (and getters for "current" state) as the selected
- // page changes. Currently there's no API to change the selected page directly; that's
- // only possible through manipulation of the bound ViewPager.
- }
-
- @Test
- fun testBottomPaddingDelegate_default() {
- val personalListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter),
- object : EmptyStateProvider {},
- { false },
- PROFILE_PERSONAL,
- null,
- null,
- inflater,
- { Optional.empty() }
- )
- val container =
- pagerAdapter
- .getActiveEmptyStateView()
- .requireViewById<View>(com.android.internal.R.id.resolver_empty_state_container)
- container.setPadding(1, 2, 3, 4)
- pagerAdapter.setupContainerPadding()
- assertThat(container.paddingLeft).isEqualTo(1)
- assertThat(container.paddingTop).isEqualTo(2)
- assertThat(container.paddingRight).isEqualTo(3)
- assertThat(container.paddingBottom).isEqualTo(4)
- }
-
- @Test
- fun testBottomPaddingDelegate_override() {
- val personalListAdapter =
- mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter),
- object : EmptyStateProvider {},
- { false },
- PROFILE_PERSONAL,
- null,
- null,
- inflater,
- { Optional.of(42) }
- )
- val container =
- pagerAdapter
- .getActiveEmptyStateView()
- .requireViewById<View>(com.android.internal.R.id.resolver_empty_state_container)
- container.setPadding(1, 2, 3, 4)
- pagerAdapter.setupContainerPadding()
- assertThat(container.paddingLeft).isEqualTo(1)
- assertThat(container.paddingTop).isEqualTo(2)
- assertThat(container.paddingRight).isEqualTo(3)
- assertThat(container.paddingBottom).isEqualTo(42)
- }
-
- @Test
- fun testPresumedQuietModeEmptyStateForWorkProfile_whenQuiet() {
- // TODO: this is "presumed" because the conditions to determine whether we "should" show an
- // empty state aren't enforced to align with the conditions when we actually *would* -- I
- // believe `shouldShowEmptyStateScreen` should be implemented in terms of the provider?
- val personalListAdapter =
- mock<ResolverListAdapter> {
- whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE)
- whenever(getUnfilteredCount()).thenReturn(1)
- }
- val workListAdapter =
- mock<ResolverListAdapter> {
- whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE)
- whenever(getUnfilteredCount()).thenReturn(1)
- }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter, workListAdapter),
- object : EmptyStateProvider {},
- { true }, // <-- Work mode is quiet.
- PROFILE_WORK,
- WORK_USER_HANDLE,
- null,
- inflater,
- { Optional.empty() }
- )
- assertThat(pagerAdapter.shouldShowEmptyStateScreen(workListAdapter)).isTrue()
- assertThat(pagerAdapter.shouldShowEmptyStateScreen(personalListAdapter)).isFalse()
- }
-
- @Test
- fun testPresumedQuietModeEmptyStateForWorkProfile_notWhenNotQuiet() {
- // TODO: this is "presumed" because the conditions to determine whether we "should" show an
- // empty state aren't enforced to align with the conditions when we actually *would* -- I
- // believe `shouldShowEmptyStateScreen` should be implemented in terms of the provider?
- val personalListAdapter =
- mock<ResolverListAdapter> {
- whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE)
- whenever(getUnfilteredCount()).thenReturn(1)
- }
- val workListAdapter =
- mock<ResolverListAdapter> {
- whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE)
- whenever(getUnfilteredCount()).thenReturn(1)
- }
- val pagerAdapter =
- MultiProfilePagerAdapter(
- { listAdapter: ResolverListAdapter -> listAdapter },
- { listView: ListView, bindAdapter: ResolverListAdapter ->
- listView.setAdapter(bindAdapter)
- },
- ImmutableList.of(personalListAdapter, workListAdapter),
- object : EmptyStateProvider {},
- { false }, // <-- Work mode is not quiet.
- PROFILE_WORK,
- WORK_USER_HANDLE,
- null,
- inflater,
- { Optional.empty() }
- )
- assertThat(pagerAdapter.shouldShowEmptyStateScreen(workListAdapter)).isFalse()
- assertThat(pagerAdapter.shouldShowEmptyStateScreen(personalListAdapter)).isFalse()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/ResolverActivityTest.java b/java/tests/src/com/android/intentresolver/v2/ResolverActivityTest.java
deleted file mode 100644
index f0911833..00000000
--- a/java/tests/src/com/android/intentresolver/v2/ResolverActivityTest.java
+++ /dev/null
@@ -1,1105 +0,0 @@
-/*
- * Copyright (C) 2016 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.v2;
-
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.action.ViewActions.swipeUp;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-import static com.android.intentresolver.MatcherUtils.first;
-import static com.android.intentresolver.v2.ResolverWrapperActivity.sOverrides;
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.espresso.Espresso;
-import androidx.test.espresso.NoMatchingViewException;
-import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.intentresolver.AnnotatedUserHandles;
-import com.android.intentresolver.R;
-import com.android.intentresolver.ResolvedComponentInfo;
-import com.android.intentresolver.ResolverDataProvider;
-import com.android.intentresolver.widget.ResolverDrawerLayout;
-import com.google.android.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Resolver activity instrumentation tests
- */
-@RunWith(AndroidJUnit4.class)
-public class ResolverActivityTest {
-
- private static final UserHandle PERSONAL_USER_HANDLE = androidx.test.platform.app
- .InstrumentationRegistry.getInstrumentation().getTargetContext().getUser();
- private static final UserHandle WORK_PROFILE_USER_HANDLE = UserHandle.of(10);
- private static final UserHandle CLONE_PROFILE_USER_HANDLE = UserHandle.of(11);
-
- protected Intent getConcreteIntentForLaunch(Intent clientIntent) {
- clientIntent.setClass(
- androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().getTargetContext(),
- ResolverWrapperActivity.class);
- return clientIntent;
- }
-
- @Rule
- public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
- new ActivityTestRule<>(ResolverWrapperActivity.class, false, false);
-
- @Before
- public void setup() {
- // TODO: use the other form of `adoptShellPermissionIdentity()` where we explicitly list the
- // permissions we require (which we'll read from the manifest at runtime).
- androidx.test.platform.app.InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .adoptShellPermissionIdentity();
-
- sOverrides.reset();
- }
-
- @Test
- public void twoOptionsAndUserSelectsOne() throws InterruptedException {
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2,
- PERSONAL_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- assertThat(activity.getAdapter().getCount(), is(2));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
- onView(withText(toChoose.activityInfo.name))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Ignore // Failing - b/144929805
- @Test
- public void setMaxHeight() throws Exception {
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2,
- PERSONAL_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
- waitForIdle();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- final View viewPager = activity.findViewById(com.android.internal.R.id.profile_pager);
- final int initialResolverHeight = viewPager.getHeight();
-
- activity.runOnUiThread(() -> {
- ResolverDrawerLayout layout = (ResolverDrawerLayout)
- activity.findViewById(
- com.android.internal.R.id.contentPanel);
- ((ResolverDrawerLayout.LayoutParams) viewPager.getLayoutParams()).maxHeight
- = initialResolverHeight - 1;
- // Force a relayout
- layout.invalidate();
- layout.requestLayout();
- });
- waitForIdle();
- assertThat("Drawer should be capped at maxHeight",
- viewPager.getHeight() == (initialResolverHeight - 1));
-
- activity.runOnUiThread(() -> {
- ResolverDrawerLayout layout = (ResolverDrawerLayout)
- activity.findViewById(
- com.android.internal.R.id.contentPanel);
- ((ResolverDrawerLayout.LayoutParams) viewPager.getLayoutParams()).maxHeight
- = initialResolverHeight + 1;
- // Force a relayout
- layout.invalidate();
- layout.requestLayout();
- });
- waitForIdle();
- assertThat("Drawer should not change height if its height is less than maxHeight",
- viewPager.getHeight() == initialResolverHeight);
- }
-
- @Ignore // Failing - b/144929805
- @Test
- public void setShowAtTopToTrue() throws Exception {
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2,
- PERSONAL_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
- waitForIdle();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- final View viewPager = activity.findViewById(com.android.internal.R.id.profile_pager);
- final View divider = activity.findViewById(com.android.internal.R.id.divider);
- final RelativeLayout profileView =
- (RelativeLayout) activity.findViewById(com.android.internal.R.id.profile_button)
- .getParent();
- assertThat("Drawer should show at bottom by default",
- profileView.getBottom() + divider.getHeight() == viewPager.getTop()
- && profileView.getTop() > 0);
-
- activity.runOnUiThread(() -> {
- ResolverDrawerLayout layout = (ResolverDrawerLayout)
- activity.findViewById(
- com.android.internal.R.id.contentPanel);
- layout.setShowAtTop(true);
- });
- waitForIdle();
- assertThat("Drawer should show at top with new attribute",
- profileView.getBottom() + divider.getHeight() == viewPager.getTop()
- && profileView.getTop() == 0);
- }
-
- @Test
- public void hasLastChosenActivity() throws Exception {
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2,
- PERSONAL_USER_HANDLE);
- ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
-
- setupResolverControllers(resolvedComponentInfos);
- when(sOverrides.resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- // The other entry is filtered to the last used slot
- assertThat(activity.getAdapter().getCount(), is(1));
- assertThat(activity.getAdapter().getPlaceholderCount(), is(1));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- onView(withId(com.android.internal.R.id.button_once)).perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test
- public void hasOtherProfileOneOption() throws Exception {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
-
- ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
- Intent sendIntent = createSendImageIntent();
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- // The other entry is filtered to the last used slot
- assertThat(activity.getAdapter().getCount(), is(1));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10,
- PERSONAL_USER_HANDLE);
- // We pick the first one as there is another one in the work profile side
- onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test
- public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- // The other entry is filtered to the other profile slot
- assertThat(activity.getAdapter().getCount(), is(2));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- // Confirm that the button bar is disabled by default
- onView(withId(com.android.internal.R.id.button_once)).check(matches(not(isEnabled())));
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2, PERSONAL_USER_HANDLE);
-
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once)).perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
-
- @Test
- public void hasLastChosenActivityAndOtherProfile() throws Exception {
- // In this case we prefer the other profile and don't display anything about the last
- // chosen activity.
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- setupResolverControllers(resolvedComponentInfos);
- when(sOverrides.resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0));
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- // The other entry is filtered to the other profile slot
- assertThat(activity.getAdapter().getCount(), is(2));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- // Confirm that the button bar is disabled by default
- onView(withId(com.android.internal.R.id.button_once)).check(matches(not(isEnabled())));
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2, PERSONAL_USER_HANDLE);
-
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once)).perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test
- public void testWorkTab_displayedWhenWorkProfileUserAvailable() {
- Intent sendIntent = createSendImageIntent();
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.tabs)).check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_hiddenWhenWorkProfileUserNotAvailable() {
- Intent sendIntent = createSendImageIntent();
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.tabs)).check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testWorkTab_workTabListPopulatedBeforeGoingToTab() throws InterruptedException {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId = */ 10,
- PERSONAL_USER_HANDLE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos,
- new ArrayList<>(workResolvedComponentInfos));
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
- // The work list adapter must be populated in advance before tapping the other tab
- assertThat(activity.getWorkListAdapter().getCount(), is(4));
- }
-
- @Test
- public void testWorkTab_workTabUsesExpectedAdapter() {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
-
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
- assertThat(activity.getWorkListAdapter().getCount(), is(4));
- }
-
- @Test
- public void testWorkTab_personalTabUsesExpectedAdapter() {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
-
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
- assertThat(activity.getPersonalListAdapter().getCount(), is(2));
- }
-
- @Test
- public void testWorkTab_workProfileHasExpectedNumberOfTargets() throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
- waitForIdle();
- assertThat(activity.getWorkListAdapter().getCount(), is(4));
- }
-
- @Test
- public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
- waitForIdle();
- onView(first(allOf(withText(workResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
-
- waitForIdle();
- assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
- }
-
- @Test
- public void testWorkTab_noPersonalApps_workTabHasExpectedNumberOfTargets()
- throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(1, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
-
- waitForIdle();
- assertThat(activity.getWorkListAdapter().getCount(), is(4));
- }
-
- @Test
- public void testWorkTab_headerIsVisibleInPersonalTab() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(1, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createOpenWebsiteIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- TextView headerText = activity.findViewById(com.android.internal.R.id.title);
- String initialText = headerText.getText().toString();
- assertFalse("Header text is empty.", initialText.isEmpty());
- assertThat(headerText.getVisibility(), is(View.VISIBLE));
- }
-
- @Test
- public void testWorkTab_switchTabs_headerStaysSame() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(1, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createOpenWebsiteIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- TextView headerText = activity.findViewById(com.android.internal.R.id.title);
- String initialText = headerText.getText().toString();
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
-
- waitForIdle();
- String currentText = headerText.getText().toString();
- assertThat(headerText.getVisibility(), is(View.VISIBLE));
- assertThat(String.format("Header text is not the same when switching tabs, personal profile"
- + " header was %s but work profile header is %s", initialText, currentText),
- TextUtils.equals(initialText, currentText));
- }
-
- @Test
- public void testWorkTab_noPersonalApps_canStartWorkApps()
- throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId= */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
- waitForIdle();
- onView(first(allOf(
- withText(workResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name),
- isDisplayed())))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
- waitForIdle();
-
- assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
- }
-
- @Test
- public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets, WORK_PROFILE_USER_HANDLE);
- sOverrides.hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
-
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_workProfileDisabled_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets, WORK_PROFILE_USER_HANDLE);
- sOverrides.isQuietModeEnabled = true;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_turn_on_work_apps))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_noWorkAppsAvailable_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0, WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_no_work_apps_available))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0, WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
- sOverrides.isQuietModeEnabled = true;
- sOverrides.hasCrossProfileIntents = false;
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testMiniResolver() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(1, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(1, WORK_PROFILE_USER_HANDLE);
- // Personal profile only has a browser
- personalResolvedComponentInfos.get(0).getResolveInfoAt(0).handleAllWebDataURI = true;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.open_cross_profile)).check(matches(isDisplayed()));
- }
-
- @Test
- public void testMiniResolver_noCurrentProfileTarget() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(0, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(1, WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- // Need to ensure mini resolver doesn't trigger here.
- assertNotMiniResolver();
- }
-
- private void assertNotMiniResolver() {
- try {
- onView(withId(com.android.internal.R.id.open_cross_profile))
- .check(matches(isDisplayed()));
- } catch (NoMatchingViewException e) {
- return;
- }
- fail("Mini resolver present but shouldn't be");
- }
-
- @Test
- public void testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3, PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0, WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
- sOverrides.isQuietModeEnabled = true;
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_no_work_apps_available))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_onePersonalTarget_emptyStateOnWorkTarget_doesNotAutoLaunch() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10,
- PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets, WORK_PROFILE_USER_HANDLE);
- sOverrides.hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
- ResolveInfo[] chosen = new ResolveInfo[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- chosen[0] = result.first.getResolveInfo();
- return true;
- };
-
- mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- assertNull(chosen[0]);
- }
-
- @Test
- public void testLayoutWithDefault_withWorkTab_neverShown() throws RemoteException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- // In this case we prefer the other profile and don't display anything about the last
- // chosen activity.
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTest(2, PERSONAL_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
- when(sOverrides.resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0));
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- // The other entry is filtered to the last used slot
- assertThat(activity.getAdapter().hasFilteredItem(), is(false));
- assertThat(activity.getAdapter().getCount(), is(2));
- assertThat(activity.getAdapter().getPlaceholderCount(), is(2));
- }
-
- @Test
- public void testClonedProfilePresent_personalAdapterIsSetWithPersonalProfile() {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ false, /* cloneAvailable= */ true);
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- setupResolverControllers(resolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- assertThat(activity.getCurrentUserHandle(), is(PERSONAL_USER_HANDLE));
- assertThat(activity.getAdapter().getCount(), is(3));
- }
-
- @Test
- public void testClonedProfilePresent_personalTabUsesExpectedAdapter() {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ true);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
- WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- assertThat(activity.getCurrentUserHandle(), is(PERSONAL_USER_HANDLE));
- assertThat(activity.getAdapter().getCount(), is(3));
- }
-
- @Test
- public void testClonedProfilePresent_layoutWithDefault_neverShown() throws Exception {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ false, /* cloneAvailable= */ true);
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 2,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
- when(sOverrides.resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- Espresso.registerIdlingResources(activity.getLabelIdlingResource());
- waitForIdle();
-
- assertThat(activity.getAdapter().hasFilteredItem(), is(false));
- assertThat(activity.getAdapter().getCount(), is(2));
- assertThat(activity.getAdapter().getPlaceholderCount(), is(2));
- }
-
- @Test
- public void testClonedProfilePresent_alwaysButtonDisabled() throws Exception {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ false, /* cloneAvailable= */ true);
- Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
-
- setupResolverControllers(resolvedComponentInfos);
- when(sOverrides.resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
-
- // Confirm that the button bar is disabled by default
- onView(withId(com.android.internal.R.id.button_once)).check(matches(not(isEnabled())));
- onView(withId(com.android.internal.R.id.button_always)).check(matches(not(isEnabled())));
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2, PERSONAL_USER_HANDLE);
-
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
- .perform(click());
-
- onView(withId(com.android.internal.R.id.button_once)).check(matches(isEnabled()));
- onView(withId(com.android.internal.R.id.button_always)).check(matches(not(isEnabled())));
- }
-
- @Test
- public void testClonedProfilePresent_personalProfileActivityIsStartedInCorrectUser()
- throws Exception {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ true);
-
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(3, WORK_PROFILE_USER_HANDLE);
- sOverrides.hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
- final UserHandle[] selectedActivityUserHandle = new UserHandle[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- selectedActivityUserHandle[0] = result.second;
- return true;
- };
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(first(allOf(withText(personalResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
- waitForIdle();
-
- assertThat(selectedActivityUserHandle[0], is(activity.getAdapter().getUserHandle()));
- }
-
- @Test
- public void testClonedProfilePresent_workProfileActivityIsStartedInCorrectUser()
- throws Exception {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ true);
-
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(3, WORK_PROFILE_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
- sendIntent.setType("TestType");
- final UserHandle[] selectedActivityUserHandle = new UserHandle[1];
- sOverrides.onSafelyStartInternalCallback = result -> {
- selectedActivityUserHandle[0] = result.second;
- return true;
- };
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab))
- .perform(click());
- waitForIdle();
- onView(first(allOf(withText(workResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
- .perform(click());
- onView(withId(com.android.internal.R.id.button_once))
- .perform(click());
- waitForIdle();
-
- assertThat(selectedActivityUserHandle[0], is(activity.getAdapter().getUserHandle()));
- }
-
- @Test
- public void testClonedProfilePresent_personalProfileResolverComparatorHasCorrectUsers()
- throws Exception {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ false, /* cloneAvailable= */ true);
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- setupResolverControllers(resolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
-
- final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
- waitForIdle();
- List<UserHandle> result = activity
- .getResolverRankerServiceUserHandleList(PERSONAL_USER_HANDLE);
-
- assertThat(result.containsAll(
- Lists.newArrayList(PERSONAL_USER_HANDLE, CLONE_PROFILE_USER_HANDLE)), is(true));
- }
-
- private Intent createSendImageIntent() {
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
- sendIntent.setType("image/jpeg");
- return sendIntent;
- }
-
- private Intent createOpenWebsiteIntent() {
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_VIEW);
- sendIntent.setData(Uri.parse("https://google.com"));
- return sendIntent;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults,
- UserHandle resolvedForUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, resolvedForUser));
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsWithCloneProfileForTest(
- int numberOfResults,
- UserHandle resolvedForPersonalUser,
- UserHandle resolvedForClonedUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < 1; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- resolvedForPersonalUser));
- }
- for (int i = 1; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- resolvedForClonedUser));
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
- int numberOfResults,
- UserHandle resolvedForUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- if (i == 0) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i,
- resolvedForUser));
- } else {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, resolvedForUser));
- }
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
- int numberOfResults, int userId, UserHandle resolvedForUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- if (i == 0) {
- infoList.add(
- ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId,
- resolvedForUser));
- } else {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, resolvedForUser));
- }
- }
- return infoList;
- }
-
- private void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- private void markOtherProfileAvailability(boolean workAvailable, boolean cloneAvailable) {
- AnnotatedUserHandles.Builder handles = AnnotatedUserHandles.newBuilder();
- handles
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(PERSONAL_USER_HANDLE)
- .setPersonalProfileUserHandle(PERSONAL_USER_HANDLE);
- if (workAvailable) {
- handles.setWorkProfileUserHandle(WORK_PROFILE_USER_HANDLE);
- }
- if (cloneAvailable) {
- handles.setCloneProfileUserHandle(CLONE_PROFILE_USER_HANDLE);
- }
- sOverrides.annotatedUserHandles = handles.build();
- }
-
- private void setupResolverControllers(
- List<ResolvedComponentInfo> personalResolvedComponentInfos) {
- setupResolverControllers(personalResolvedComponentInfos, new ArrayList<>());
- }
-
- private void setupResolverControllers(
- List<ResolvedComponentInfo> personalResolvedComponentInfos,
- List<ResolvedComponentInfo> workResolvedComponentInfos) {
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .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/v2/ResolverWrapperActivity.java b/java/tests/src/com/android/intentresolver/v2/ResolverWrapperActivity.java
deleted file mode 100644
index 92b73d92..00000000
--- a/java/tests/src/com/android/intentresolver/v2/ResolverWrapperActivity.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2017 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.v2;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-import androidx.test.espresso.idling.CountingIdlingResource;
-
-import com.android.intentresolver.AnnotatedUserHandles;
-import com.android.intentresolver.ResolverListAdapter;
-import com.android.intentresolver.ResolverListController;
-import com.android.intentresolver.WorkProfileAvailabilityManager;
-import com.android.intentresolver.chooser.DisplayResolveInfo;
-import com.android.intentresolver.chooser.SelectableTargetInfo;
-import com.android.intentresolver.chooser.TargetInfo;
-import com.android.intentresolver.emptystate.CrossProfileIntentsChecker;
-import com.android.intentresolver.icons.LabelInfo;
-import com.android.intentresolver.icons.TargetDataLoader;
-
-import kotlin.Unit;
-
-import java.util.List;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-/*
- * Simple wrapper around chooser activity to be able to initiate it under test
- */
-public class ResolverWrapperActivity extends ResolverActivity {
- static final OverrideData sOverrides = new OverrideData();
-
- private final CountingIdlingResource mLabelIdlingResource =
- new CountingIdlingResource("LoadLabelTask");
-
- public ResolverWrapperActivity() {
- super(/* isIntentPicker= */ true);
- mLogic = new TestResolverActivityLogic(
- "ResolverWrapper",
- () -> this,
- () -> {
- onWorkProfileStatusUpdated();
- return Unit.INSTANCE;
- },
- sOverrides
- );
- }
-
- public CountingIdlingResource getLabelIdlingResource() {
- return mLabelIdlingResource;
- }
-
- @Override
- public ResolverListAdapter createResolverListAdapter(
- Context context,
- List<Intent> payloadIntents,
- Intent[] initialIntents,
- List<ResolveInfo> rList,
- boolean filterLastUsed,
- UserHandle userHandle,
- TargetDataLoader targetDataLoader) {
- return new ResolverListAdapter(
- context,
- payloadIntents,
- initialIntents,
- rList,
- filterLastUsed,
- createListController(userHandle),
- userHandle,
- payloadIntents.get(0), // TODO: extract upstream
- this,
- userHandle,
- new TargetDataLoaderWrapper(targetDataLoader, mLabelIdlingResource));
- }
-
- @Override
- protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
- if (sOverrides.mCrossProfileIntentsChecker != null) {
- return sOverrides.mCrossProfileIntentsChecker;
- }
- return super.createCrossProfileIntentsChecker();
- }
-
- ResolverListAdapter getAdapter() {
- return mMultiProfilePagerAdapter.getActiveListAdapter();
- }
-
- ResolverListAdapter getPersonalListAdapter() {
- return ((ResolverListAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(0));
- }
-
- ResolverListAdapter getWorkListAdapter() {
- if (mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
- return null;
- }
- return ((ResolverListAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(1));
- }
-
- @Override
- public boolean isVoiceInteraction() {
- if (sOverrides.isVoiceInteraction != null) {
- return sOverrides.isVoiceInteraction;
- }
- return super.isVoiceInteraction();
- }
-
- @Override
- public void safelyStartActivityInternal(TargetInfo cti, UserHandle user,
- @Nullable Bundle options) {
- if (sOverrides.onSafelyStartInternalCallback != null
- && sOverrides.onSafelyStartInternalCallback.apply(new Pair<>(cti, user))) {
- return;
- }
- super.safelyStartActivityInternal(cti, user, options);
- }
-
- @Override
- protected ResolverListController createListController(UserHandle userHandle) {
- if (userHandle == UserHandle.SYSTEM) {
- return sOverrides.resolverListController;
- }
- return sOverrides.workResolverListController;
- }
-
- @Override
- public PackageManager getPackageManager() {
- if (sOverrides.createPackageManager != null) {
- return sOverrides.createPackageManager.apply(super.getPackageManager());
- }
- return super.getPackageManager();
- }
-
- protected UserHandle getCurrentUserHandle() {
- return mMultiProfilePagerAdapter.getCurrentUserHandle();
- }
-
- @Override
- public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
- super.startActivityAsUser(intent, options, user);
- }
-
- @Override
- protected List<UserHandle> getResolverRankerServiceUserHandleListInternal(UserHandle
- userHandle) {
- return super.getResolverRankerServiceUserHandleListInternal(userHandle);
- }
-
- /**
- * We cannot directly mock the activity created since instrumentation creates it.
- * <p>
- * Instead, we use static instances of this object to modify behavior.
- */
- public static class OverrideData {
- @SuppressWarnings("Since15")
- public Function<PackageManager, PackageManager> createPackageManager;
- public Function<Pair<TargetInfo, UserHandle>, Boolean> onSafelyStartInternalCallback;
- public ResolverListController resolverListController;
- public ResolverListController workResolverListController;
- public Boolean isVoiceInteraction;
- public AnnotatedUserHandles annotatedUserHandles;
- public Integer myUserId;
- public boolean hasCrossProfileIntents;
- public boolean isQuietModeEnabled;
- public WorkProfileAvailabilityManager mWorkProfileAvailability;
- public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
-
- public void reset() {
- onSafelyStartInternalCallback = null;
- isVoiceInteraction = null;
- createPackageManager = null;
- resolverListController = mock(ResolverListController.class);
- workResolverListController = mock(ResolverListController.class);
- annotatedUserHandles = AnnotatedUserHandles.newBuilder()
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(UserHandle.SYSTEM)
- .setPersonalProfileUserHandle(UserHandle.SYSTEM)
- .build();
- myUserId = null;
- hasCrossProfileIntents = true;
- isQuietModeEnabled = false;
-
- mWorkProfileAvailability = new WorkProfileAvailabilityManager(null, null, null) {
- @Override
- public boolean isQuietModeEnabled() {
- return isQuietModeEnabled;
- }
-
- @Override
- public boolean isWorkProfileUserUnlocked() {
- return true;
- }
-
- @Override
- public void requestQuietModeEnabled(boolean enabled) {
- isQuietModeEnabled = enabled;
- }
-
- @Override
- public void markWorkProfileEnabledBroadcastReceived() {}
-
- @Override
- public boolean isWaitingToEnableWorkProfile() {
- return false;
- }
- };
-
- mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class);
- when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt()))
- .thenAnswer(invocation -> hasCrossProfileIntents);
- }
- }
-
- private static class TargetDataLoaderWrapper extends TargetDataLoader {
- private final TargetDataLoader mTargetDataLoader;
- private final CountingIdlingResource mLabelIdlingResource;
-
- private TargetDataLoaderWrapper(
- TargetDataLoader targetDataLoader, CountingIdlingResource labelIdlingResource) {
- mTargetDataLoader = targetDataLoader;
- mLabelIdlingResource = labelIdlingResource;
- }
-
- @Override
- public void loadAppTargetIcon(
- @NonNull DisplayResolveInfo info,
- @NonNull UserHandle userHandle,
- @NonNull Consumer<Drawable> callback) {
- mTargetDataLoader.loadAppTargetIcon(info, userHandle, callback);
- }
-
- @Override
- public void loadDirectShareIcon(
- @NonNull SelectableTargetInfo info,
- @NonNull UserHandle userHandle,
- @NonNull Consumer<Drawable> callback) {
- mTargetDataLoader.loadDirectShareIcon(info, userHandle, callback);
- }
-
- @Override
- public void loadLabel(
- @NonNull DisplayResolveInfo info,
- @NonNull Consumer<LabelInfo> callback) {
- mLabelIdlingResource.increment();
- mTargetDataLoader.loadLabel(
- info,
- (result) -> {
- mLabelIdlingResource.decrement();
- callback.accept(result);
- });
- }
-
- @Override
- public void getOrLoadLabel(@NonNull DisplayResolveInfo info) {
- mTargetDataLoader.getOrLoadLabel(info);
- }
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/TestChooserActivityLogic.kt b/java/tests/src/com/android/intentresolver/v2/TestChooserActivityLogic.kt
deleted file mode 100644
index 198b9236..00000000
--- a/java/tests/src/com/android/intentresolver/v2/TestChooserActivityLogic.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.android.intentresolver.v2
-
-import androidx.activity.ComponentActivity
-import com.android.intentresolver.AnnotatedUserHandles
-import com.android.intentresolver.WorkProfileAvailabilityManager
-import com.android.intentresolver.icons.TargetDataLoader
-
-/** Activity logic for use when testing [ChooserActivity]. */
-class TestChooserActivityLogic(
- tag: String,
- activityProvider: () -> ComponentActivity,
- onWorkProfileStatusUpdated: () -> Unit,
- targetDataLoaderProvider: () -> TargetDataLoader,
- onPreinitialization: () -> Unit,
- private val overrideData: ChooserActivityOverrideData,
-) :
- ChooserActivityLogic(
- tag,
- activityProvider,
- onWorkProfileStatusUpdated,
- targetDataLoaderProvider,
- onPreinitialization,
- ) {
-
- override val annotatedUserHandles: AnnotatedUserHandles? by lazy {
- overrideData.annotatedUserHandles
- }
-
- override val workProfileAvailabilityManager: WorkProfileAvailabilityManager by lazy {
- overrideData.mWorkProfileAvailability ?: super.workProfileAvailabilityManager
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/TestResolverActivityLogic.kt b/java/tests/src/com/android/intentresolver/v2/TestResolverActivityLogic.kt
deleted file mode 100644
index 7581043e..00000000
--- a/java/tests/src/com/android/intentresolver/v2/TestResolverActivityLogic.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.android.intentresolver.v2
-
-import androidx.activity.ComponentActivity
-import com.android.intentresolver.AnnotatedUserHandles
-import com.android.intentresolver.WorkProfileAvailabilityManager
-
-/** Activity logic for use when testing [ResolverActivity]. */
-class TestResolverActivityLogic(
- tag: String,
- activityProvider: () -> ComponentActivity,
- onWorkProfileStatusUpdated: () -> Unit,
- private val overrideData: ResolverWrapperActivity.OverrideData,
-) : ResolverActivityLogic(tag, activityProvider, onWorkProfileStatusUpdated) {
-
- override val annotatedUserHandles: AnnotatedUserHandles? by lazy {
- overrideData.annotatedUserHandles
- }
-
- override val workProfileAvailabilityManager: WorkProfileAvailabilityManager by lazy {
- overrideData.mWorkProfileAvailability ?: super.workProfileAvailabilityManager
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/UnbundledChooserActivityTest.java b/java/tests/src/com/android/intentresolver/v2/UnbundledChooserActivityTest.java
deleted file mode 100644
index 5245f655..00000000
--- a/java/tests/src/com/android/intentresolver/v2/UnbundledChooserActivityTest.java
+++ /dev/null
@@ -1,3147 +0,0 @@
-/*
- * Copyright (C) 2016 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.v2;
-
-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;
-import static androidx.test.espresso.matcher.ViewMatchers.hasSibling;
-import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-
-import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_CHOOSER_TARGET;
-import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_DEFAULT;
-import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
-import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
-import static com.android.intentresolver.ChooserListAdapter.CALLER_TARGET_SCORE_BOOST;
-import static com.android.intentresolver.ChooserListAdapter.SHORTCUT_TARGET_SCORE_BOOST;
-import static com.android.intentresolver.MatcherUtils.first;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static junit.framework.Assert.assertNull;
-
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-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;
-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;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.Icon;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.provider.DeviceConfig;
-import android.service.chooser.ChooserAction;
-import android.service.chooser.ChooserTarget;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.style.BackgroundColorSpan;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.StyleSpan;
-import android.text.style.UnderlineSpan;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-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.espresso.matcher.ViewMatchers;
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-
-import com.android.intentresolver.AnnotatedUserHandles;
-import com.android.intentresolver.ChooserListAdapter;
-import com.android.intentresolver.Flags;
-import com.android.intentresolver.IChooserWrapper;
-import com.android.intentresolver.R;
-import com.android.intentresolver.ResolvedComponentInfo;
-import com.android.intentresolver.ResolverDataProvider;
-import com.android.intentresolver.TestContentProvider;
-import com.android.intentresolver.TestPreviewImageLoader;
-import com.android.intentresolver.chooser.DisplayResolveInfo;
-import com.android.intentresolver.contentpreview.ImageLoader;
-import com.android.intentresolver.logging.EventLog;
-import com.android.intentresolver.logging.FakeEventLog;
-import com.android.intentresolver.shortcuts.ShortcutLoader;
-import com.android.intentresolver.v2.platform.ImageEditor;
-import com.android.intentresolver.v2.platform.ImageEditorModule;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import dagger.hilt.android.testing.BindValue;
-import dagger.hilt.android.testing.HiltAndroidRule;
-import dagger.hilt.android.testing.HiltAndroidTest;
-import dagger.hilt.android.testing.UninstallModules;
-
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.Matchers;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-/**
- * Instrumentation tests for ChooserActivity.
- * <p>
- * Legacy test suite migrated from framework CoreTests.
- */
-@RunWith(Parameterized.class)
-@HiltAndroidTest
-@UninstallModules(ImageEditorModule.class)
-public class UnbundledChooserActivityTest {
-
- private static FakeEventLog getEventLog(ChooserWrapperActivity activity) {
- return (FakeEventLog) activity.mEventLog;
- }
-
- private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
- .getInstrumentation().getTargetContext().getUser();
- private static final UserHandle WORK_PROFILE_USER_HANDLE = UserHandle.of(10);
- private static final UserHandle CLONE_PROFILE_USER_HANDLE = UserHandle.of(11);
-
- private static final Function<PackageManager, PackageManager> DEFAULT_PM = pm -> pm;
- private static final Function<PackageManager, PackageManager> NO_APP_PREDICTION_SERVICE_PM =
- pm -> {
- PackageManager mock = Mockito.spy(pm);
- when(mock.getAppPredictionServicePackageName()).thenReturn(null);
- return mock;
- };
-
- @Parameterized.Parameters
- public static Collection packageManagers() {
- return Arrays.asList(new Object[][] {
- // Default PackageManager
- { DEFAULT_PM },
- // No App Prediction Service
- { NO_APP_PREDICTION_SERVICE_PM}
- });
- }
-
- private static final String TEST_MIME_TYPE = "application/TestType";
-
- private static final int CONTENT_PREVIEW_IMAGE = 1;
- private static final int CONTENT_PREVIEW_FILE = 2;
- private static final int CONTENT_PREVIEW_TEXT = 3;
-
- @Rule(order = 0)
- public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Rule(order = 1)
- public HiltAndroidRule mHiltAndroidRule = new HiltAndroidRule(this);
-
- @Rule(order = 2)
- public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
- new ActivityTestRule<>(ChooserWrapperActivity.class, false, false);
-
- @Before
- public void setUp() {
- // TODO: use the other form of `adoptShellPermissionIdentity()` where we explicitly list the
- // permissions we require (which we'll read from the manifest at runtime).
- InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .adoptShellPermissionIdentity();
-
- cleanOverrideData();
- mHiltAndroidRule.inject();
- }
-
- private final Function<PackageManager, PackageManager> mPackageManagerOverride;
-
- /** An arbitrary pre-installed activity that handles this type of intent. */
- @BindValue
- @ImageEditor
- final Optional<ComponentName> mImageEditor = Optional.ofNullable(
- ComponentName.unflattenFromString("com.google.android.apps.messaging/"
- + ".ui.conversationlist.ShareIntentActivity"));
-
- public UnbundledChooserActivityTest(
- Function<PackageManager, PackageManager> packageManagerOverride) {
- mPackageManagerOverride = packageManagerOverride;
- }
-
- private void setDeviceConfigProperty(
- @NonNull String propertyName,
- @NonNull String value) {
- // TODO: consider running with {@link #runWithShellPermissionIdentity()} to more narrowly
- // request WRITE_DEVICE_CONFIG permissions if we get rid of the broad grant we currently
- // configure in {@link #setup()}.
- // TODO: is it really appropriate that this is always set with makeDefault=true?
- boolean valueWasSet = DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- propertyName,
- value,
- true /* makeDefault */);
- if (!valueWasSet) {
- throw new IllegalStateException(
- "Could not set " + propertyName + " to " + value);
- }
- }
-
- public void cleanOverrideData() {
- ChooserActivityOverrideData.getInstance().reset();
- ChooserActivityOverrideData.getInstance().createPackageManager = mPackageManagerOverride;
-
- setDeviceConfigProperty(
- SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
- Boolean.toString(true));
- }
-
- @Test
- public void customTitle() throws InterruptedException {
- Intent viewIntent = createViewTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- final IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(
- Intent.createChooser(viewIntent, "chooser test"));
-
- waitForIdle();
- assertThat(activity.getAdapter().getCount(), is(2));
- assertThat(activity.getAdapter().getServiceTargetCount(), is(0));
- onView(withId(android.R.id.title)).check(matches(withText("chooser test")));
- }
-
- @Test
- public void customTitleIgnoredForSendIntents() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "chooser test"));
- waitForIdle();
- onView(withId(android.R.id.title))
- .check(matches(withText(R.string.whichSendApplication)));
- }
-
- @Test
- public void emptyTitle() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(android.R.id.title))
- .check(matches(withText(R.string.whichSendApplication)));
- }
-
- @Test
- public void test_shareRichTextWithRichTitle_richTextAndRichTitleDisplayed() {
- CharSequence title = new SpannableStringBuilder()
- .append("Rich", new UnderlineSpan(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
- .append(
- "Title",
- new ForegroundColorSpan(Color.RED),
- Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
- CharSequence sharedText = new SpannableStringBuilder()
- .append(
- "Rich",
- new BackgroundColorSpan(Color.YELLOW),
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
- .append(
- "Text",
- new StyleSpan(Typeface.ITALIC),
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- Intent sendIntent = createSendTextIntent();
- sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText);
- sendIntent.putExtra(Intent.EXTRA_TITLE, title);
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check((view, e) -> {
- assertThat(view).isInstanceOf(TextView.class);
- CharSequence text = ((TextView) view).getText();
- assertThat(text).isInstanceOf(Spanned.class);
- Spanned spanned = (Spanned) text;
- assertThat(spanned.getSpans(0, spanned.length(), Object.class))
- .hasLength(2);
- assertThat(spanned.getSpans(0, 4, UnderlineSpan.class)).hasLength(1);
- assertThat(spanned.getSpans(4, spanned.length(), ForegroundColorSpan.class))
- .hasLength(1);
- });
-
- onView(withId(com.android.internal.R.id.content_preview_text))
- .check((view, e) -> {
- assertThat(view).isInstanceOf(TextView.class);
- CharSequence text = ((TextView) view).getText();
- assertThat(text).isInstanceOf(Spanned.class);
- Spanned spanned = (Spanned) text;
- assertThat(spanned.getSpans(0, spanned.length(), Object.class))
- .hasLength(2);
- assertThat(spanned.getSpans(0, 4, BackgroundColorSpan.class)).hasLength(1);
- assertThat(spanned.getSpans(4, spanned.length(), StyleSpan.class)).hasLength(1);
- });
- }
-
- @Test
- public void emptyPreviewTitleAndThumbnail() throws InterruptedException {
- Intent sendIntent = createSendTextIntentWithPreview(null, null);
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check(matches(not(isDisplayed())));
- onView(withId(com.android.internal.R.id.content_preview_thumbnail))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void visiblePreviewTitleWithoutThumbnail() throws InterruptedException {
- String previewTitle = "My Content Preview Title";
- Intent sendIntent = createSendTextIntentWithPreview(previewTitle, null);
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check(matches(withText(previewTitle)));
- onView(withId(com.android.internal.R.id.content_preview_thumbnail))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void visiblePreviewTitleWithInvalidThumbnail() throws InterruptedException {
- String previewTitle = "My Content Preview Title";
- Intent sendIntent = createSendTextIntentWithPreview(previewTitle,
- Uri.parse("tel:(+49)12345789"));
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.content_preview_thumbnail))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void visiblePreviewTitleAndThumbnail() throws InterruptedException {
- String previewTitle = "My Content Preview Title";
- Uri uri = Uri.parse(
- "android.resource://com.android.frameworks.coretests/"
- + com.android.intentresolver.tests.R.drawable.test320x240);
- Intent sendIntent = createSendTextIntentWithPreview(previewTitle, uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(com.android.internal.R.id.content_preview_title))
- .check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.content_preview_thumbnail))
- .check(matches(isDisplayed()));
- }
-
- @Test @Ignore
- public void twoOptionsAndUserSelectsOne() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- assertThat(activity.getAdapter().getCount(), is(2));
- onView(withId(com.android.internal.R.id.profile_button)).check(doesNotExist());
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
- onView(withText(toChoose.activityInfo.name))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test @Ignore
- public void fourOptionsStackedIntoOneTarget() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
-
- // create just enough targets to ensure the a-z list should be shown
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1);
-
- // next create 4 targets in a single app that should be stacked into a single target
- String packageName = "xxx.yyy";
- String appName = "aaa";
- ComponentName cn = new ComponentName(packageName, appName);
- Intent intent = new Intent("fakeIntent");
- List<ResolvedComponentInfo> infosToStack = new ArrayList<>();
- for (int i = 0; i < 4; i++) {
- ResolveInfo resolveInfo = ResolverDataProvider.createResolveInfo(i,
- UserHandle.USER_CURRENT, PERSONAL_USER_HANDLE);
- resolveInfo.activityInfo.applicationInfo.name = appName;
- resolveInfo.activityInfo.applicationInfo.packageName = packageName;
- resolveInfo.activityInfo.packageName = packageName;
- resolveInfo.activityInfo.name = "ccc" + i;
- infosToStack.add(new ResolvedComponentInfo(cn, intent, resolveInfo));
- }
- resolvedComponentInfos.addAll(infosToStack);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- // expect 1 unique targets + 1 group + 4 ranked app targets
- assertThat(activity.getAdapter().getCount(), is(6));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- onView(allOf(withText(appName), hasSibling(withText("")))).perform(click());
- waitForIdle();
-
- // clicking will launch a dialog to choose the activity within the app
- onView(withText(appName)).check(matches(isDisplayed()));
- int i = 0;
- for (ResolvedComponentInfo rci: infosToStack) {
- onView(withText("ccc" + i)).check(matches(isDisplayed()));
- ++i;
- }
- }
-
- @Test @Ignore
- public void updateChooserCountsAndModelAfterUserSelection() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- UsageStatsManager usm = activity.getUsageStatsManager();
- verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1))
- .topK(any(List.class), anyInt());
- assertThat(activity.getIsSelected(), is(false));
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- return true;
- };
- ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
- DisplayResolveInfo testDri =
- activity.createTestDisplayResolveInfo(
- sendIntent, toChoose, "testLabel", "testInfo", sendIntent);
- onView(withText(toChoose.activityInfo.name))
- .perform(click());
- waitForIdle();
- verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1))
- .updateChooserCounts(Mockito.anyString(), any(UserHandle.class),
- Mockito.anyString());
- verify(ChooserActivityOverrideData.getInstance().resolverListController, times(1))
- .updateModel(testDri);
- assertThat(activity.getIsSelected(), is(true));
- }
-
- @Ignore // b/148158199
- @Test
- public void noResultsFromPackageManager() {
- setupResolverControllers(null);
- Intent sendIntent = createSendTextIntent();
- final ChooserActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- final IChooserWrapper wrapper = (IChooserWrapper) activity;
-
- waitForIdle();
- assertThat(activity.isFinishing(), is(false));
-
- onView(withId(android.R.id.empty)).check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.profile_pager)).check(matches(not(isDisplayed())));
- InstrumentationRegistry.getInstrumentation().runOnMainSync(
- () -> wrapper.getAdapter().handlePackagesChanged()
- );
- // backward compatibility. looks like we finish when data is empty after package change
- assertThat(activity.isFinishing(), is(true));
- }
-
- @Test
- public void autoLaunchSingleResult() throws InterruptedException {
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1);
- setupResolverControllers(resolvedComponentInfos);
-
- Intent sendIntent = createSendTextIntent();
- final ChooserActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- assertThat(chosen[0], is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
- assertThat(activity.isFinishing(), is(true));
- }
-
- @Test @Ignore
- public void hasOtherProfileOneOption() {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
- Intent sendIntent = createSendTextIntent();
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- // The other entry is filtered to the other profile slot
- assertThat(activity.getAdapter().getCount(), is(1));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10);
- waitForIdle();
-
- onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test @Ignore
- public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- setupResolverControllers(resolvedComponentInfos);
- when(ChooserActivityOverrideData.getInstance().resolverListController.getLastChosen())
- .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- // The other entry is filtered to the other profile slot
- assertThat(activity.getAdapter().getCount(), is(2));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(3);
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test @Ignore
- public void hasLastChosenActivityAndOtherProfile() throws Exception {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- // The other entry is filtered to the last used slot
- assertThat(activity.getAdapter().getCount(), is(2));
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- // Make a stable copy of the components as the original list may be modified
- List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(3);
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(toChoose));
- }
-
- @Test
- @Ignore("b/285309527")
- public void testFilePlusTextSharing_ExcludeText() {
- Uri uri = createTestContentProviderUri(null, "image/png");
- Intent sendIntent = createSendImageIntent(uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
- sendIntent.putExtra(Intent.EXTRA_TEXT, "https://google.com/search?q=google");
-
- List<ResolvedComponentInfo> resolvedComponentInfos = Arrays.asList(
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.imageviewer", "ImageTarget"),
- sendIntent, PERSONAL_USER_HANDLE),
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.textviewer", "UriTarget"),
- new Intent("VIEW_TEXT"), PERSONAL_USER_HANDLE)
- );
-
- 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.content_preview_text)).check(matches(withText("File only")));
-
- AtomicReference<Intent> launchedIntentRef = new AtomicReference<>();
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = 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
- @Ignore("b/285309527")
- public void testFilePlusTextSharing_RemoveAndAddBackText() {
- Uri uri = createTestContentProviderUri("application/pdf", "image/png");
- Intent sendIntent = createSendImageIntent(uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
- 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, PERSONAL_USER_HANDLE),
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.textviewer", "UriTarget"),
- new Intent("VIEW_TEXT"), PERSONAL_USER_HANDLE)
- );
-
- 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.content_preview_text)).check(matches(withText("File only")));
-
- onView(withId(R.id.include_text_action))
- .perform(click());
- waitForIdle();
-
- onView(withId(R.id.content_preview_text)).check(matches(withText(text)));
-
- AtomicReference<Intent> launchedIntentRef = new AtomicReference<>();
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = 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
- @Ignore("b/285309527")
- public void testFilePlusTextSharing_TextExclusionDoesNotAffectAlternativeIntent() {
- Uri uri = createTestContentProviderUri("image/png", null);
- Intent sendIntent = createSendImageIntent(uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
- 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, PERSONAL_USER_HANDLE),
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.textviewer", "UriTarget"),
- alternativeIntent, PERSONAL_USER_HANDLE)
- );
-
- 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().onSafelyStartInternalCallback = 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
- @Ignore("b/285309527")
- public void testImagePlusTextSharing_failedThumbnailAndExcludedText_textChanges() {
- Uri uri = createTestContentProviderUri("image/png", null);
- Intent sendIntent = createSendImageIntent(uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- new TestPreviewImageLoader(Collections.emptyMap());
- sendIntent.putExtra(Intent.EXTRA_TEXT, "https://google.com/search?q=google");
-
- List<ResolvedComponentInfo> resolvedComponentInfos = Arrays.asList(
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.imageviewer", "ImageTarget"),
- sendIntent, PERSONAL_USER_HANDLE),
- ResolverDataProvider.createResolvedComponentInfo(
- new ComponentName("org.textviewer", "UriTarget"),
- new Intent("VIEW_TEXT"), PERSONAL_USER_HANDLE)
- );
-
- 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.image_view))
- .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)));
- onView(withId(R.id.content_preview_text))
- .check(matches(allOf(isDisplayed(), withText("Image only"))));
- }
-
- @Test
- public void copyTextToClipboard() {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final ChooserActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(R.id.copy)).check(matches(isDisplayed()));
- onView(withId(R.id.copy)).perform(click());
- ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
- Context.CLIPBOARD_SERVICE);
- ClipData clipData = clipboard.getPrimaryClip();
- assertThat(clipData).isNotNull();
- assertThat(clipData.getItemAt(0).getText()).isEqualTo("testing intent sending");
-
- ClipDescription clipDescription = clipData.getDescription();
- assertThat("text/plain", is(clipDescription.getMimeType(0)));
-
- assertEquals(mActivityRule.getActivityResult().getResultCode(), RESULT_OK);
- }
-
- @Test
- public void copyTextToClipboardLogging() {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(R.id.copy)).check(matches(isDisplayed()));
- onView(withId(R.id.copy)).perform(click());
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getActionSelected())
- .isEqualTo(new FakeEventLog.ActionSelected(
- /* targetType = */ EventLog.SELECTION_TYPE_COPY));
- }
-
- @Test
- @Ignore
- public void testNearbyShareLogging() throws Exception {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.chooser_nearby_button))
- .check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.chooser_nearby_button)).perform(click());
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
- @Test @Ignore
- public void testEditImageLogs() {
-
- Uri uri = createTestContentProviderUri("image/png", null);
- Intent sendIntent = createSendImageIntent(uri);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.chooser_edit_button)).check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.chooser_edit_button)).perform(click());
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
-
- @Test
- public void oneVisibleImagePreview() {
- Uri uri = createTestContentProviderUri("image/png", null);
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createWideBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.scrollable_image_preview))
- .check((view, exception) -> {
- if (exception != null) {
- throw exception;
- }
- RecyclerView recyclerView = (RecyclerView) view;
- assertThat(recyclerView.getAdapter().getItemCount(), is(1));
- assertThat(recyclerView.getChildCount(), is(1));
- View imageView = recyclerView.getChildAt(0);
- Rect rect = new Rect();
- boolean isPartiallyVisible = imageView.getGlobalVisibleRect(rect);
- assertThat(
- "image preview view is not fully visible",
- isPartiallyVisible
- && rect.width() == imageView.getWidth()
- && rect.height() == imageView.getHeight());
- });
- }
-
- @Test
- public void allThumbnailsFailedToLoad_hidePreview() {
- Uri uri = createTestContentProviderUri("image/jpg", null);
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- new TestPreviewImageLoader(Collections.emptyMap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.scrollable_image_preview))
- .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)));
- }
-
- @Test(timeout = 4_000)
- public void testSlowUriMetadata_fallbackToFilePreview() {
- Uri uri = createTestContentProviderUri(
- "application/pdf", "image/png", /*streamTypeTimeout=*/8_000);
- ArrayList<Uri> uris = new ArrayList<>(1);
- uris.add(uri);
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- // The preview type resolution is expected to timeout and default to file preview, otherwise
- // the test should timeout.
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("image.png")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
- @Test(timeout = 4_000)
- public void testSendManyFilesWithSmallMetadataDelayAndOneImage_fallbackToFilePreviewUi() {
- Uri fileUri = createTestContentProviderUri(
- "application/pdf", "application/pdf", /*streamTypeTimeout=*/300);
- Uri imageUri = createTestContentProviderUri("application/pdf", "image/png");
- ArrayList<Uri> uris = new ArrayList<>(50);
- for (int i = 0; i < 49; i++) {
- uris.add(fileUri);
- }
- uris.add(imageUri);
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(imageUri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos);
- // The preview type resolution is expected to timeout and default to file preview, otherwise
- // the test should timeout.
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
-
- waitForIdle();
-
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("image.png")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
- @Test
- public void testManyVisibleImagePreview_ScrollableImagePreview() {
- Uri uri = createTestContentProviderUri("image/png", null);
-
- 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().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.scrollable_image_preview))
- .perform(RecyclerViewActions.scrollToLastPosition())
- .check((view, exception) -> {
- if (exception != null) {
- throw exception;
- }
- RecyclerView recyclerView = (RecyclerView) view;
- assertThat(recyclerView.getAdapter().getItemCount(), is(uris.size()));
- });
- }
-
- @Test(timeout = 4_000)
- public void testPartiallyLoadedMetadata_previewIsShownForTheLoadedPart() {
- Uri imgOneUri = createTestContentProviderUri("image/png", null);
- Uri imgTwoUri = createTestContentProviderUri("image/png", null)
- .buildUpon()
- .path("image-2.png")
- .build();
- Uri docUri = createTestContentProviderUri("application/pdf", "image/png", 8_000);
- ArrayList<Uri> uris = new ArrayList<>(2);
- // two large previews to fill the screen and be presented right away and one
- // document that would be delayed by the URI metadata reading
- uris.add(imgOneUri);
- uris.add(imgTwoUri);
- uris.add(docUri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- Map<Uri, Bitmap> bitmaps = new HashMap<>();
- bitmaps.put(imgOneUri, createWideBitmap(Color.RED));
- bitmaps.put(imgTwoUri, createWideBitmap(Color.GREEN));
- bitmaps.put(docUri, createWideBitmap(Color.BLUE));
- ChooserActivityOverrideData.getInstance().imageLoader =
- new TestPreviewImageLoader(bitmaps);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos);
-
- // the preview type is expected to be resolved quickly based on the first provided URI
- // metadata. If, instead, it is dependent on the third URI metadata, the test should either
- // timeout or (more probably due to inner timeout) default to file preview type; anyway the
- // test will fail.
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(R.id.scrollable_image_preview))
- .check((view, exception) -> {
- if (exception != null) {
- throw exception;
- }
- RecyclerView recyclerView = (RecyclerView) view;
- assertThat(recyclerView.getChildCount()).isAtLeast(1);
- // the first view is a preview
- View imageView = recyclerView.getChildAt(0).findViewById(R.id.image);
- assertThat(imageView).isNotNull();
- })
- .perform(RecyclerViewActions.scrollToLastPosition())
- .check((view, exception) -> {
- if (exception != null) {
- throw exception;
- }
- RecyclerView recyclerView = (RecyclerView) view;
- assertThat(recyclerView.getChildCount()).isAtLeast(1);
- // check that the last view is a loading indicator
- View loadingIndicator =
- recyclerView.getChildAt(recyclerView.getChildCount() - 1);
- assertThat(loadingIndicator).isNotNull();
- });
- waitForIdle();
- }
-
- @Test
- public void testImageAndTextPreview() {
- final Uri uri = createTestContentProviderUri("image/png", null);
- 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().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withText(sharedText))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void test_shareImageWithRichText_RichTextIsDisplayed() {
- final Uri uri = createTestContentProviderUri("image/png", null);
- final CharSequence sharedText = new SpannableStringBuilder()
- .append(
- "text-",
- new StyleSpan(Typeface.BOLD_ITALIC),
- Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
- .append(
- Long.toString(System.currentTimeMillis()),
- new ForegroundColorSpan(Color.RED),
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withText(sharedText.toString()))
- .check(matches(isDisplayed()))
- .check((view, e) -> {
- if (e != null) {
- throw e;
- }
- assertThat(view).isInstanceOf(TextView.class);
- CharSequence text = ((TextView) view).getText();
- assertThat(text).isInstanceOf(Spanned.class);
- Spanned spanned = (Spanned) text;
- Object[] spans = spanned.getSpans(0, text.length(), Object.class);
- assertThat(spans).hasLength(2);
- assertThat(spanned.getSpans(0, 5, StyleSpan.class)).hasLength(1);
- assertThat(spanned.getSpans(5, text.length(), ForegroundColorSpan.class))
- .hasLength(1);
- });
- }
-
- @Test
- public void testTextPreviewWhenTextIsSharedWithMultipleImages() {
- final Uri uri = createTestContentProviderUri("image/png", null);
- final String sharedText = "text-" + System.currentTimeMillis();
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- sendIntent.putExtra(Intent.EXTRA_TEXT, sharedText);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- Mockito.any(UserHandle.class)))
- .thenReturn(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);
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test"));
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- FakeEventLog.ChooserActivityShown event = eventLog.getChooserActivityShown();
- assertThat(event).isNotNull();
- assertThat(event.isWorkProfile()).isFalse();
- assertThat(event.getTargetMimeType()).isEqualTo(TEST_MIME_TYPE);
- }
-
- @Test
- public void testOnCreateLoggingFromWorkProfile() {
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
- ChooserActivityOverrideData.getInstance().alternateProfileSetting =
- MetricsEvent.MANAGED_PROFILE;
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test"));
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- FakeEventLog.ChooserActivityShown event = eventLog.getChooserActivityShown();
- assertThat(event).isNotNull();
- assertThat(event.isWorkProfile()).isTrue();
- assertThat(event.getTargetMimeType()).isEqualTo(TEST_MIME_TYPE);
- }
-
- @Test
- public void testEmptyPreviewLogging() {
- Intent sendIntent = createSendTextIntentWithPreview(null, null);
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent,
- "empty preview logger test"));
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- FakeEventLog.ChooserActivityShown event = eventLog.getChooserActivityShown();
- assertThat(event).isNotNull();
- assertThat(event.isWorkProfile()).isFalse();
- assertThat(event.getTargetMimeType()).isNull();
- }
-
- @Test
- public void testTitlePreviewLogging() {
- Intent sendIntent = createSendTextIntentWithPreview("TestTitle", null);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getActionShareWithPreview())
- .isEqualTo(new FakeEventLog.ActionShareWithPreview(
- /* previewType = */ CONTENT_PREVIEW_TEXT));
- }
-
- @Test
- public void testImagePreviewLogging() {
- Uri uri = createTestContentProviderUri("image/png", null);
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createBitmap());
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getActionShareWithPreview())
- .isEqualTo(new FakeEventLog.ActionShareWithPreview(
- /* previewType = */ CONTENT_PREVIEW_IMAGE));
- }
-
- @Test
- public void oneVisibleFilePreview() throws InterruptedException {
- Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
-
- @Test
- public void moreThanOneVisibleFilePreview() throws InterruptedException {
- Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
- uris.add(uri);
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
- onView(withId(R.id.content_preview_more_files)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_more_files)).check(matches(withText("+ 2 more files")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
- @Test
- public void contentProviderThrowSecurityException() throws InterruptedException {
- Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos);
-
- ChooserActivityOverrideData.getInstance().resolverForceException = true;
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
- @Test
- public void contentProviderReturnsNoColumns() throws InterruptedException {
- Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf");
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
-
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos);
-
- Cursor cursor = mock(Cursor.class);
- when(cursor.getCount()).thenReturn(1);
- Mockito.doNothing().when(cursor).close();
- when(cursor.moveToFirst()).thenReturn(true);
- when(cursor.getColumnIndex(Mockito.anyString())).thenReturn(-1);
-
- ChooserActivityOverrideData.getInstance().resolverCursor = cursor;
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
- onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf")));
- onView(withId(R.id.content_preview_more_files)).check(matches(isDisplayed()));
- onView(withId(R.id.content_preview_more_files)).check(matches(withText("+ 1 more file")));
- onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
- }
-
- @Test
- public void testGetBaseScore() {
- final float testBaseScore = 0.89f;
-
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getScore(Mockito.isA(DisplayResolveInfo.class)))
- .thenReturn(testBaseScore);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- final DisplayResolveInfo testDri =
- activity.createTestDisplayResolveInfo(
- sendIntent,
- ResolverDataProvider.createResolveInfo(3, 0, PERSONAL_USER_HANDLE),
- "testLabel",
- "testInfo",
- sendIntent);
- final ChooserListAdapter adapter = activity.getAdapter();
-
- assertThat(adapter.getBaseScore(null, 0), is(CALLER_TARGET_SCORE_BOOST));
- assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_DEFAULT), is(testBaseScore));
- assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_CHOOSER_TARGET), is(testBaseScore));
- assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE),
- is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST));
- assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER),
- is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST));
- }
-
- // This test is too long and too slow and should not be taken as an example for future tests.
- @Test
- public void testDirectTargetSelectionLogging() {
- 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 =
- createShortcutLoaderFactory();
-
- // Start activity
- ChooserWrapperActivity activity =
- 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)).updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- List<ChooserTarget> serviceTargets = createDirectShareTargets(1, "");
- ShortcutLoader.Result result = new ShortcutLoader.Result(
- true,
- 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();
-
- final ChooserListAdapter activeAdapter = activity.getAdapter();
- assertThat(
- "Chooser should have 3 targets (2 apps, 1 direct)",
- activeAdapter.getCount(),
- is(3));
- assertThat(
- "Chooser should have exactly one selectable direct target",
- activeAdapter.getSelectableServiceTargetCount(),
- is(1));
- assertThat(
- "The resolver info must match the resolver info used to create the target",
- activeAdapter.getItem(0).getResolveInfo(),
- is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
-
- // Click on the direct target
- String name = serviceTargets.get(0).getTitle().toString();
- onView(withText(name))
- .perform(click());
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getShareTargetSelected()).hasSize(1);
- FakeEventLog.ShareTargetSelected call = eventLog.getShareTargetSelected().get(0);
- assertThat(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE);
- assertThat(call.getDirectTargetAlsoRanked()).isEqualTo(-1);
- var hashResult = call.getDirectTargetHashed();
- var hash = hashResult == null ? "" : hashResult.hashedString;
- assertWithMessage("Hash is not predictable but must be obfuscated")
- .that(hash).isNotEqualTo(name);
- }
-
- // This test is too long and too slow and should not be taken as an example for future tests.
- @Test
- public void testDirectTargetLoggingWithRankedAppTarget() {
- 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 =
- createShortcutLoaderFactory();
-
- // Start activity
- ChooserWrapperActivity activity =
- 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)).updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- List<ChooserTarget> serviceTargets = createDirectShareTargets(
- 1,
- resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
- ShortcutLoader.Result result = new ShortcutLoader.Result(
- true,
- 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();
-
- final ChooserListAdapter activeAdapter = activity.getAdapter();
- assertThat(
- "Chooser should have 3 targets (2 apps, 1 direct)",
- activeAdapter.getCount(),
- is(3));
- assertThat(
- "Chooser should have exactly one selectable direct target",
- activeAdapter.getSelectableServiceTargetCount(),
- is(1));
- assertThat(
- "The resolver info must match the resolver info used to create the target",
- activeAdapter.getItem(0).getResolveInfo(),
- is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
-
- // Click on the direct target
- String name = serviceTargets.get(0).getTitle().toString();
- onView(withText(name))
- .perform(click());
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getShareTargetSelected()).hasSize(1);
- FakeEventLog.ShareTargetSelected call = eventLog.getShareTargetSelected().get(0);
-
- assertThat(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE);
- assertThat(call.getDirectTargetAlsoRanked()).isEqualTo(0);
- }
-
- @Test
- public void testShortcutTargetWithApplyAppLimits() {
- // Set up resources
- Resources resources = Mockito.spy(
- InstrumentationRegistry.getInstrumentation().getContext().getResources());
- ChooserActivityOverrideData.getInstance().resources = resources;
- doReturn(1).when(resources).getInteger(R.integer.config_maxShortcutTargetsPerApp);
- 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 =
- createShortcutLoaderFactory();
-
- // 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)).updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- List<ChooserTarget> serviceTargets = createDirectShareTargets(
- 2,
- resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
- ShortcutLoader.Result result = new ShortcutLoader.Result(
- true,
- 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();
-
- final ChooserListAdapter activeAdapter = activity.getAdapter();
- assertThat(
- "Chooser should have 3 targets (2 apps, 1 direct)",
- activeAdapter.getCount(),
- is(3));
- assertThat(
- "Chooser should have exactly one selectable direct target",
- activeAdapter.getSelectableServiceTargetCount(),
- is(1));
- assertThat(
- "The resolver info must match the resolver info used to create the target",
- activeAdapter.getItem(0).getResolveInfo(),
- is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
- assertThat(
- "The display label must match",
- activeAdapter.getItem(0).getDisplayLabel(),
- is("testTitle0"));
- }
-
- @Test
- public void testShortcutTargetWithoutApplyAppLimits() {
- setDeviceConfigProperty(
- SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
- Boolean.toString(false));
- // Set up resources
- Resources resources = Mockito.spy(
- InstrumentationRegistry.getInstrumentation().getContext().getResources());
- ChooserActivityOverrideData.getInstance().resources = resources;
- doReturn(1).when(resources).getInteger(R.integer.config_maxShortcutTargetsPerApp);
- 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 =
- createShortcutLoaderFactory();
-
- // 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)).updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- List<ChooserTarget> serviceTargets = createDirectShareTargets(
- 2,
- resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
- ShortcutLoader.Result result = new ShortcutLoader.Result(
- true,
- 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();
-
- final ChooserListAdapter activeAdapter = activity.getAdapter();
- assertThat(
- "Chooser should have 4 targets (2 apps, 2 direct)",
- activeAdapter.getCount(),
- is(4));
- assertThat(
- "Chooser should have exactly two selectable direct target",
- activeAdapter.getSelectableServiceTargetCount(),
- is(2));
- assertThat(
- "The resolver info must match the resolver info used to create the target",
- activeAdapter.getItem(0).getResolveInfo(),
- is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
- assertThat(
- "The display label must match",
- activeAdapter.getItem(0).getDisplayLabel(),
- is("testTitle0"));
- assertThat(
- "The display label must match",
- activeAdapter.getItem(1).getDisplayLabel(),
- is("testTitle1"));
- }
-
- @Test
- public void testLaunchWithCallerProvidedTarget() {
- setDeviceConfigProperty(
- SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
- Boolean.toString(false));
- // Set up resources
- Resources resources = Mockito.spy(
- InstrumentationRegistry.getInstrumentation().getContext().getResources());
- ChooserActivityOverrideData.getInstance().resources = resources;
- doReturn(1).when(resources).getInteger(R.integer.config_maxShortcutTargetsPerApp);
-
- // We need app targets for direct targets to get displayed
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
- setupResolverControllers(resolvedComponentInfos, resolvedComponentInfos);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- // set caller-provided target
- Intent chooserIntent = Intent.createChooser(createSendTextIntent(), null);
- String callerTargetLabel = "Caller Target";
- ChooserTarget[] targets = new ChooserTarget[] {
- new ChooserTarget(
- callerTargetLabel,
- Icon.createWithBitmap(createBitmap()),
- 0.1f,
- resolvedComponentInfos.get(0).name,
- new Bundle())
- };
- chooserIntent.putExtra(Intent.EXTRA_CHOOSER_TARGETS, targets);
-
- // create test shortcut loader factory, remember loaders and their callbacks
- SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>> shortcutLoaders =
- createShortcutLoaderFactory();
-
- // Start activity
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
-
- // verify that ShortcutLoader was queried
- ArgumentCaptor<DisplayResolveInfo[]> appTargets =
- ArgumentCaptor.forClass(DisplayResolveInfo[].class);
- verify(shortcutLoaders.get(0).first, times(1)).updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- ShortcutLoader.Result result = new ShortcutLoader.Result(
- true,
- appTargets.getValue(),
- new ShortcutLoader.ShortcutResultInfo[0],
- new HashMap<>(),
- new HashMap<>());
- activity.getMainExecutor().execute(() -> shortcutLoaders.get(0).second.accept(result));
- waitForIdle();
-
- final ChooserListAdapter activeAdapter = activity.getAdapter();
- assertThat(
- "Chooser should have 3 targets (2 apps, 1 direct)",
- activeAdapter.getCount(),
- is(3));
- assertThat(
- "Chooser should have exactly two selectable direct target",
- activeAdapter.getSelectableServiceTargetCount(),
- is(1));
- assertThat(
- "The display label must match",
- activeAdapter.getItem(0).getDisplayLabel(),
- is(callerTargetLabel));
-
- // Switch to work profile and ensure that the target *doesn't* show up there.
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- for (int i = 0; i < activity.getWorkListAdapter().getCount(); i++) {
- assertThat(
- "Chooser target should not show up in opposite profile",
- activity.getWorkListAdapter().getItem(i).getDisplayLabel(),
- not(callerTargetLabel));
- }
- }
-
- @Test
- 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),
- Context.RECEIVER_EXPORTED);
-
- try {
- onView(withText(customActionLabel)).perform(click());
- assertTrue("Timeout waiting for broadcast",
- broadcastInvoked.await(5000, TimeUnit.MILLISECONDS));
- } finally {
- testContext.unregisterReceiver(testReceiver);
- }
- }
-
- @Test
- 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);
- String label = "modify share";
- PendingIntent pendingIntent = PendingIntent.getBroadcast(
- testContext,
- 123,
- new Intent(modifyShareAction),
- PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
- ChooserAction action = new ChooserAction.Builder(Icon.createWithBitmap(
- createBitmap()), label, pendingIntent).build();
- chooserIntent.putExtra(
- Intent.EXTRA_CHOOSER_MODIFY_SHARE_ACTION,
- action);
- // 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),
- Context.RECEIVER_EXPORTED);
-
- try {
- onView(withText(label)).perform(click());
- assertTrue("Timeout waiting for broadcast",
- broadcastInvoked.await(5000, TimeUnit.MILLISECONDS));
-
- } finally {
- testContext.unregisterReceiver(testReceiver);
- }
- }
-
- @Test
- public void testUpdateMaxTargetsPerRow_columnCountIsUpdated() throws InterruptedException {
- updateMaxTargetsPerRowResource(/* targetsPerRow= */ 4);
- givenAppTargets(/* appCount= */ 16);
- Intent sendIntent = createSendTextIntent();
- final ChooserActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
-
- updateMaxTargetsPerRowResource(/* targetsPerRow= */ 6);
- InstrumentationRegistry.getInstrumentation()
- .runOnMainSync(() -> activity.onConfigurationChanged(
- InstrumentationRegistry.getInstrumentation()
- .getContext().getResources().getConfiguration()));
-
- waitForIdle();
- onView(withId(com.android.internal.R.id.resolver_list))
- .check(matches(withGridColumnCount(6)));
- }
-
- // This test is too long and too slow and should not be taken as an example for future tests.
- @Test @Ignore
- public void testDirectTargetLoggingWithAppTargetNotRankedPortrait()
- throws InterruptedException {
- testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_PORTRAIT, 4);
- }
-
- @Test @Ignore
- public void testDirectTargetLoggingWithAppTargetNotRankedLandscape()
- throws InterruptedException {
- testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_LANDSCAPE, 8);
- }
-
- private void testDirectTargetLoggingWithAppTargetNotRanked(
- int orientation, int appTargetsExpected) {
- Configuration configuration =
- new Configuration(InstrumentationRegistry.getInstrumentation().getContext()
- .getResources().getConfiguration());
- configuration.orientation = orientation;
-
- Resources resources = Mockito.spy(
- InstrumentationRegistry.getInstrumentation().getContext().getResources());
- ChooserActivityOverrideData.getInstance().resources = resources;
- doReturn(configuration).when(resources).getConfiguration();
-
- Intent sendIntent = createSendTextIntent();
- // We need app targets for direct targets to get displayed
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(15);
- setupResolverControllers(resolvedComponentInfos);
-
- // Create direct share target
- List<ChooserTarget> serviceTargets = createDirectShareTargets(1,
- resolvedComponentInfos.get(14).getResolveInfoAt(0).activityInfo.packageName);
- ResolveInfo ri = ResolverDataProvider.createResolveInfo(16, 0, PERSONAL_USER_HANDLE);
-
- // Start activity
- ChooserWrapperActivity activity =
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- // Insert the direct share target
- Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
- directShareToShortcutInfos.put(serviceTargets.get(0), null);
- InstrumentationRegistry.getInstrumentation().runOnMainSync(
- () -> activity.getAdapter().addServiceResults(
- activity.createTestDisplayResolveInfo(sendIntent,
- ri,
- "testLabel",
- "testInfo",
- sendIntent),
- serviceTargets,
- TARGET_TYPE_CHOOSER_TARGET,
- directShareToShortcutInfos,
- /* directShareToAppTargets */ null)
- );
-
- assertThat(
- String.format("Chooser should have %d targets (%d apps, 1 direct, 15 A-Z)",
- appTargetsExpected + 16, appTargetsExpected),
- activity.getAdapter().getCount(), is(appTargetsExpected + 16));
- assertThat("Chooser should have exactly one selectable direct target",
- activity.getAdapter().getSelectableServiceTargetCount(), is(1));
- assertThat("The resolver info must match the resolver info used to create the target",
- activity.getAdapter().getItem(0).getResolveInfo(), is(ri));
-
- // Click on the direct target
- String name = serviceTargets.get(0).getTitle().toString();
- onView(withText(name))
- .perform(click());
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- var invocations = eventLog.getShareTargetSelected();
- assertWithMessage("Only one ShareTargetSelected event logged")
- .that(invocations).hasSize(1);
- FakeEventLog.ShareTargetSelected call = invocations.get(0);
- assertWithMessage("targetType should be SELECTION_TYPE_SERVICE")
- .that(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE);
- assertWithMessage(
- "The packages shouldn't match for app target and direct target")
- .that(call.getDirectTargetAlsoRanked()).isEqualTo(-1);
- }
-
- @Test
- public void testWorkTab_displayedWhenWorkProfileUserAvailable() {
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
-
- onView(withId(android.R.id.tabs)).check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_hiddenWhenWorkProfileUserNotAvailable() {
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
-
- onView(withId(android.R.id.tabs)).check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testWorkTab_eachTabUsesExpectedAdapter() {
- int personalProfileTargets = 3;
- int otherProfileTargets = 1;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(
- personalProfileTargets + otherProfileTargets, /* userID */ 10);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(
- workProfileTargets);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
-
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
- onView(withText(R.string.resolver_work_tab)).perform(click());
- assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
- assertThat(activity.getPersonalListAdapter().getCount(), is(personalProfileTargets));
- assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets));
- }
-
- @Test
- public void testWorkTab_workProfileHasExpectedNumberOfTargets() throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets));
- }
-
- @Test @Ignore
- public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(first(allOf(
- withText(workResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name),
- isDisplayed())))
- .perform(click());
- waitForIdle();
- assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
- }
-
- @Test
- public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
-
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_workProfileDisabled_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- ChooserActivityOverrideData.getInstance().isQuietModeEnabled = true;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_turn_on_work_apps))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_noWorkAppsAvailable_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_no_work_apps_available))
- .check(matches(isDisplayed()));
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_SCROLLABLE_PREVIEW)
- public void testWorkTab_previewIsScrollable() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(300);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
-
- Uri uri = createTestContentProviderUri("image/png", null);
-
- ArrayList<Uri> uris = new ArrayList<>();
- uris.add(uri);
-
- Intent sendIntent = createSendUriIntentWithPreview(uris);
- ChooserActivityOverrideData.getInstance().imageLoader =
- createImageLoader(uri, createWideBitmap());
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "Scrollable preview test"));
- waitForIdle();
-
- onView(withId(com.android.intentresolver.R.id.scrollable_image_preview))
- .check(matches(isDisplayed()));
-
- onView(withId(com.android.internal.R.id.contentPanel)).perform(swipeUp());
- waitForIdle();
-
- onView(withId(com.android.intentresolver.R.id.chooser_headline_row_container))
- .check(matches(isCompletelyDisplayed()));
- onView(withId(com.android.intentresolver.R.id.headline))
- .check(matches(isDisplayed()));
- onView(withId(com.android.intentresolver.R.id.scrollable_image_preview))
- .check(matches(not(isDisplayed())));
- }
-
- @Ignore // b/220067877
- @Test
- public void testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- ChooserActivityOverrideData.getInstance().isQuietModeEnabled = true;
- ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- ChooserActivityOverrideData.getInstance().isQuietModeEnabled = true;
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_no_work_apps_available))
- .check(matches(isDisplayed()));
- }
-
- @Test @Ignore("b/222124533")
- public void testAppTargetLogging() throws InterruptedException {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- // TODO(b/222124533): other test cases use a timeout to make sure that the UI is fully
- // populated; without one, this test flakes. Ideally we should address the need for a
- // timeout everywhere instead of introducing one to fix this particular test.
-
- assertThat(activity.getAdapter().getCount(), is(2));
- onView(withId(com.android.internal.R.id.profile_button)).check(doesNotExist());
-
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
- onView(withText(toChoose.activityInfo.name))
- .perform(click());
- waitForIdle();
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
- @Test
- public void testDirectTargetLogging() {
- 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
- ChooserWrapperActivity activity =
- 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))
- .updateAppTargets(appTargets.capture());
-
- // send shortcuts
- assertThat(
- "Wrong number of app targets",
- appTargets.getValue().length,
- is(resolvedComponentInfos.size()));
- 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();
-
- assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
- activity.getAdapter().getCount(), is(3));
- assertThat("Chooser should have exactly one selectable direct target",
- activity.getAdapter().getSelectableServiceTargetCount(), is(1));
- assertThat(
- "The resolver info must match the resolver info used to create the target",
- activity.getAdapter().getItem(0).getResolveInfo(),
- is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
-
- // Click on the direct target
- String name = serviceTargets.get(0).getTitle().toString();
- onView(withText(name))
- .perform(click());
- waitForIdle();
-
- FakeEventLog eventLog = getEventLog(activity);
- assertThat(eventLog.getShareTargetSelected()).hasSize(1);
- FakeEventLog.ShareTargetSelected call = eventLog.getShareTargetSelected().get(0);
- assertThat(call.getTargetType()).isEqualTo(EventLog.SELECTION_TYPE_SERVICE);
- }
-
- @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))
- .updateAppTargets(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);
- setupResolverControllers(resolvedComponentInfos);
-
- // Start activity
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
-
- // Thread.sleep shouldn't be a thing in an integration test but it's
- // necessary here because of the way the code is structured
- Thread.sleep(3000);
-
- assertThat("Chooser should have 2 app targets",
- activity.getAdapter().getCount(), is(2));
- assertThat("Chooser should have no direct targets",
- activity.getAdapter().getSelectableServiceTargetCount(), is(0));
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
- @Ignore // b/220067877
- @Test
- public void testCopyTextToClipboardLogging() throws Exception {
- Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
-
- setupResolverControllers(resolvedComponentInfos);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- waitForIdle();
-
- onView(withId(com.android.internal.R.id.chooser_copy_button)).check(matches(isDisplayed()));
- onView(withId(com.android.internal.R.id.chooser_copy_button)).perform(click());
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
- @Test @Ignore("b/222124533")
- public void testSwitchProfileLogging() throws InterruptedException {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
- onView(withText(R.string.resolver_personal_tab)).perform(click());
- waitForIdle();
-
- // TODO(b/211669337): Determine the expected SHARESHEET_DIRECT_LOAD_COMPLETE events.
- }
-
- @Test
- public void testWorkTab_onePersonalTarget_emptyStateOnWorkTarget_doesNotAutoLaunch() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "Test"));
- waitForIdle();
-
- assertNull(chosen[0]);
- }
-
- @Test
- public void testOneInitialIntent_noAutolaunch() {
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(1);
- setupResolverControllers(personalResolvedComponentInfos);
- Intent chooserIntent = createChooserIntent(createSendTextIntent(),
- new Intent[] {new Intent("action.fake")});
- ResolveInfo[] chosen = new ResolveInfo[1];
- ChooserActivityOverrideData.getInstance().onSafelyStartInternalCallback = targetInfo -> {
- chosen[0] = targetInfo.getResolveInfo();
- return true;
- };
- ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
- ResolveInfo ri = createFakeResolveInfo();
- when(
- ChooserActivityOverrideData
- .getInstance().packageManager
- .resolveActivity(any(Intent.class), any()))
- .thenReturn(ri);
- waitForIdle();
-
- IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
-
- assertNull(chosen[0]);
- assertThat(activity
- .getPersonalListAdapter().getCallerTargetCount(), is(1));
- }
-
- @Test
- public void testWorkTab_withInitialIntents_workTabDoesNotIncludePersonalInitialIntents() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 1;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent[] initialIntents = {
- new Intent("action.fake1"),
- new Intent("action.fake2")
- };
- Intent chooserIntent = createChooserIntent(createSendTextIntent(), initialIntents);
- ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .packageManager
- .resolveActivity(any(Intent.class), any()))
- .thenReturn(createFakeResolveInfo());
- waitForIdle();
-
- IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
-
- assertThat(activity.getPersonalListAdapter().getCallerTargetCount(), is(2));
- assertThat(activity.getWorkListAdapter().getCallerTargetCount(), is(0));
- }
-
- @Test
- public void testWorkTab_xProfileIntentsDisabled_personalToWork_nonSendIntent_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets);
- ChooserActivityOverrideData.getInstance().hasCrossProfileIntents = false;
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent[] initialIntents = {
- new Intent("action.fake1"),
- new Intent("action.fake2")
- };
- Intent chooserIntent = createChooserIntent(new Intent(), initialIntents);
- ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .packageManager
- .resolveActivity(any(Intent.class), any()))
- .thenReturn(createFakeResolveInfo());
-
- mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
-
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testWorkTab_noWorkAppsAvailable_nonSendIntent_emptyStateShown() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(0);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent[] initialIntents = {
- new Intent("action.fake1"),
- new Intent("action.fake2")
- };
- Intent chooserIntent = createChooserIntent(new Intent(), initialIntents);
- ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .packageManager
- .resolveActivity(any(Intent.class), any()))
- .thenReturn(createFakeResolveInfo());
-
- mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- onView(withText(R.string.resolver_no_work_apps_available))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testDeduplicateCallerTargetRankedTarget() {
- // Create 4 ranked app targets.
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(4);
- setupResolverControllers(personalResolvedComponentInfos);
- // Create caller target which is duplicate with one of app targets
- Intent chooserIntent = createChooserIntent(createSendTextIntent(),
- new Intent[] {new Intent("action.fake")});
- ChooserActivityOverrideData.getInstance().packageManager = mock(PackageManager.class);
- ResolveInfo ri = ResolverDataProvider.createResolveInfo(0,
- UserHandle.USER_CURRENT, PERSONAL_USER_HANDLE);
- when(
- ChooserActivityOverrideData
- .getInstance()
- .packageManager
- .resolveActivity(any(Intent.class), any()))
- .thenReturn(ri);
- waitForIdle();
-
- IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(chooserIntent);
- waitForIdle();
-
- // Total 4 targets (1 caller target, 3 ranked targets)
- assertThat(activity.getAdapter().getCount(), is(4));
- assertThat(activity.getAdapter().getCallerTargetCount(), is(1));
- assertThat(activity.getAdapter().getRankedTargetCount(), is(3));
- }
-
- @Test
- public void test_query_shortcut_loader_for_the_selected_tab() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- ShortcutLoader personalProfileShortcutLoader = mock(ShortcutLoader.class);
- ShortcutLoader workProfileShortcutLoader = mock(ShortcutLoader.class);
- final SparseArray<ShortcutLoader> shortcutLoaders = new SparseArray<>();
- shortcutLoaders.put(0, personalProfileShortcutLoader);
- shortcutLoaders.put(10, workProfileShortcutLoader);
- ChooserActivityOverrideData.getInstance().shortcutLoaderFactory =
- (userHandle, callback) -> shortcutLoaders.get(userHandle.getIdentifier(), null);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
- waitForIdle();
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- waitForIdle();
-
- verify(personalProfileShortcutLoader, times(1)).updateAppTargets(any());
-
- onView(withText(R.string.resolver_work_tab)).perform(click());
- waitForIdle();
-
- verify(workProfileShortcutLoader, times(1)).updateAppTargets(any());
- }
-
- @Test
- public void testClonedProfilePresent_personalAdapterIsSetWithPersonalProfile() {
- // enable cloneProfile
- markOtherProfileAvailability(/* workAvailable= */ false, /* cloneAvailable= */ true);
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsWithCloneProfileForTest(
- 3,
- PERSONAL_USER_HANDLE,
- CLONE_PROFILE_USER_HANDLE);
- setupResolverControllers(resolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
-
- final IChooserWrapper activity = (IChooserWrapper) mActivityRule
- .launchActivity(Intent.createChooser(sendIntent, "personalProfileTest"));
- waitForIdle();
-
- assertThat(activity.getPersonalListAdapter().getUserHandle(), is(PERSONAL_USER_HANDLE));
- assertThat(activity.getAdapter().getCount(), is(3));
- }
-
- @Test
- public void testClonedProfilePresent_personalTabUsesExpectedAdapter() {
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ true);
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
- List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(
- 4);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- Intent sendIntent = createSendTextIntent();
- sendIntent.setType(TEST_MIME_TYPE);
-
-
- final IChooserWrapper activity = (IChooserWrapper)
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "multi tab test"));
- waitForIdle();
-
- assertThat(activity.getCurrentUserHandle(), is(PERSONAL_USER_HANDLE));
- }
-
- private Intent createChooserIntent(Intent intent, Intent[] initialIntents) {
- Intent chooserIntent = new Intent();
- chooserIntent.setAction(Intent.ACTION_CHOOSER);
- chooserIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
- chooserIntent.putExtra(Intent.EXTRA_TITLE, "some title");
- chooserIntent.putExtra(Intent.EXTRA_INTENT, intent);
- chooserIntent.setType("text/plain");
- if (initialIntents != null) {
- chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, initialIntents);
- }
- return chooserIntent;
- }
-
- /* This is a "test of a test" to make sure that our inherited test class
- * is successfully configured to operate on the unbundled-equivalent
- * ChooserWrapperActivity.
- *
- * TODO: remove after unbundling is complete.
- */
- @Test
- public void testWrapperActivityHasExpectedConcreteType() {
- final ChooserActivity activity = mActivityRule.launchActivity(
- Intent.createChooser(new Intent("ACTION_FOO"), "foo"));
- waitForIdle();
- assertThat(activity).isInstanceOf(ChooserWrapperActivity.class);
- }
-
- private ResolveInfo createFakeResolveInfo() {
- ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.name = "FakeActivityName";
- ri.activityInfo.packageName = "fake.package.name";
- ri.activityInfo.applicationInfo = new ApplicationInfo();
- ri.activityInfo.applicationInfo.packageName = "fake.package.name";
- ri.userHandle = UserHandle.CURRENT;
- return ri;
- }
-
- private Intent createSendTextIntent() {
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
- sendIntent.setType("text/plain");
- return sendIntent;
- }
-
- private Intent createSendImageIntent(Uri imageThumbnail) {
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_STREAM, imageThumbnail);
- sendIntent.setType("image/png");
- if (imageThumbnail != null) {
- ClipData.Item clipItem = new ClipData.Item(imageThumbnail);
- sendIntent.setClipData(new ClipData("Clip Label", new String[]{"image/png"}, clipItem));
- }
-
- return sendIntent;
- }
-
- private Uri createTestContentProviderUri(
- @Nullable String mimeType, @Nullable String streamType) {
- return createTestContentProviderUri(mimeType, streamType, 0);
- }
-
- private Uri createTestContentProviderUri(
- @Nullable String mimeType, @Nullable String streamType, long streamTypeTimeout) {
- String packageName =
- InstrumentationRegistry.getInstrumentation().getContext().getPackageName();
- Uri.Builder builder = Uri.parse("content://" + packageName + "/image.png")
- .buildUpon();
- if (mimeType != null) {
- builder.appendQueryParameter(TestContentProvider.PARAM_MIME_TYPE, mimeType);
- }
- if (streamType != null) {
- builder.appendQueryParameter(TestContentProvider.PARAM_STREAM_TYPE, streamType);
- }
- if (streamTypeTimeout > 0) {
- builder.appendQueryParameter(
- TestContentProvider.PARAM_STREAM_TYPE_TIMEOUT,
- Long.toString(streamTypeTimeout));
- }
- return builder.build();
- }
-
- private Intent createSendTextIntentWithPreview(String title, Uri imageThumbnail) {
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
- sendIntent.putExtra(Intent.EXTRA_TITLE, title);
- if (imageThumbnail != null) {
- ClipData.Item clipItem = new ClipData.Item(imageThumbnail);
- sendIntent.setClipData(new ClipData("Clip Label", new String[]{"image/png"}, clipItem));
- }
-
- return sendIntent;
- }
-
- private Intent createSendUriIntentWithPreview(ArrayList<Uri> uris) {
- Intent sendIntent = new Intent();
-
- if (uris.size() > 1) {
- sendIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
- sendIntent.putExtra(Intent.EXTRA_STREAM, uris);
- } else {
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_STREAM, uris.get(0));
- }
-
- return sendIntent;
- }
-
- private Intent createViewTextIntent() {
- Intent viewIntent = new Intent();
- viewIntent.setAction(Intent.ACTION_VIEW);
- viewIntent.putExtra(Intent.EXTRA_TEXT, "testing intent viewing");
- return viewIntent;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, PERSONAL_USER_HANDLE));
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsWithCloneProfileForTest(
- int numberOfResults,
- UserHandle resolvedForPersonalUser,
- UserHandle resolvedForClonedUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < 1; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- resolvedForPersonalUser));
- }
- for (int i = 1; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- resolvedForClonedUser));
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
- int numberOfResults) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- if (i == 0) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i,
- PERSONAL_USER_HANDLE));
- } else {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- PERSONAL_USER_HANDLE));
- }
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
- int numberOfResults, int userId) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- if (i == 0) {
- infoList.add(
- ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId,
- PERSONAL_USER_HANDLE));
- } else {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i,
- PERSONAL_USER_HANDLE));
- }
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithUserId(
- int numberOfResults, int userId) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId,
- PERSONAL_USER_HANDLE));
- }
- return infoList;
- }
-
- private List<ChooserTarget> createDirectShareTargets(int numberOfResults, String packageName) {
- Icon icon = Icon.createWithBitmap(createBitmap());
- String testTitle = "testTitle";
- List<ChooserTarget> targets = new ArrayList<>();
- for (int i = 0; i < numberOfResults; i++) {
- ComponentName componentName;
- if (packageName.isEmpty()) {
- componentName = ResolverDataProvider.createComponentName(i);
- } else {
- componentName = new ComponentName(packageName, packageName + ".class");
- }
- ChooserTarget tempTarget = new ChooserTarget(
- testTitle + i,
- icon,
- (float) (1 - ((i + 1) / 10.0)),
- componentName,
- null);
- targets.add(tempTarget);
- }
- return targets;
- }
-
- private void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- private Bitmap createBitmap() {
- return createBitmap(200, 200);
- }
-
- private Bitmap createWideBitmap() {
- return createWideBitmap(Color.RED);
- }
-
- private Bitmap createWideBitmap(int bgColor) {
- WindowManager windowManager = InstrumentationRegistry.getInstrumentation()
- .getTargetContext()
- .getSystemService(WindowManager.class);
- int width = 3000;
- if (windowManager != null) {
- Rect bounds = windowManager.getMaximumWindowMetrics().getBounds();
- width = bounds.width() + 200;
- }
- return createBitmap(width, 100, bgColor);
- }
-
- private Bitmap createBitmap(int width, int height) {
- return createBitmap(width, height, Color.RED);
- }
-
- private Bitmap createBitmap(int width, int height, int bgColor) {
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
-
- Paint paint = new Paint();
- paint.setColor(bgColor);
- paint.setStyle(Paint.Style.FILL);
- canvas.drawPaint(paint);
-
- paint.setColor(Color.WHITE);
- paint.setAntiAlias(true);
- paint.setTextSize(14.f);
- paint.setTextAlign(Paint.Align.CENTER);
- canvas.drawText("Hi!", (width / 2.f), (height / 2.f), paint);
-
- return bitmap;
- }
-
- private List<ShareShortcutInfo> createShortcuts(Context context) {
- Intent testIntent = new Intent("TestIntent");
-
- List<ShareShortcutInfo> shortcuts = new ArrayList<>();
- shortcuts.add(new ShareShortcutInfo(
- new ShortcutInfo.Builder(context, "shortcut1")
- .setIntent(testIntent).setShortLabel("label1").setRank(3).build(), // 0 2
- new ComponentName("package1", "class1")));
- shortcuts.add(new ShareShortcutInfo(
- new ShortcutInfo.Builder(context, "shortcut2")
- .setIntent(testIntent).setShortLabel("label2").setRank(7).build(), // 1 3
- new ComponentName("package2", "class2")));
- shortcuts.add(new ShareShortcutInfo(
- new ShortcutInfo.Builder(context, "shortcut3")
- .setIntent(testIntent).setShortLabel("label3").setRank(1).build(), // 2 0
- new ComponentName("package3", "class3")));
- shortcuts.add(new ShareShortcutInfo(
- new ShortcutInfo.Builder(context, "shortcut4")
- .setIntent(testIntent).setShortLabel("label4").setRank(3).build(), // 3 2
- new ComponentName("package4", "class4")));
-
- return shortcuts;
- }
-
- private void markOtherProfileAvailability(boolean workAvailable, boolean cloneAvailable) {
- AnnotatedUserHandles.Builder handles = AnnotatedUserHandles.newBuilder();
- handles
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(PERSONAL_USER_HANDLE)
- .setPersonalProfileUserHandle(PERSONAL_USER_HANDLE);
- if (workAvailable) {
- handles.setWorkProfileUserHandle(WORK_PROFILE_USER_HANDLE);
- }
- if (cloneAvailable) {
- handles.setCloneProfileUserHandle(CLONE_PROFILE_USER_HANDLE);
- }
- ChooserWrapperActivity.sOverrides.annotatedUserHandles = handles.build();
- }
-
- private void setupResolverControllers(
- List<ResolvedComponentInfo> personalResolvedComponentInfos) {
- setupResolverControllers(personalResolvedComponentInfos, new ArrayList<>());
- }
-
- private void setupResolverControllers(
- List<ResolvedComponentInfo> personalResolvedComponentInfos,
- List<ResolvedComponentInfo> workResolvedComponentInfos) {
- when(
- ChooserActivityOverrideData
- .getInstance()
- .resolverListController
- .getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(
- ChooserActivityOverrideData
- .getInstance()
- .workResolverListController
- .getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(
- ChooserActivityOverrideData
- .getInstance()
- .workResolverListController
- .getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.of(10))))
- .thenReturn(new ArrayList<>(workResolvedComponentInfos));
- }
-
- private static GridRecyclerSpanCountMatcher withGridColumnCount(int columnCount) {
- return new GridRecyclerSpanCountMatcher(Matchers.is(columnCount));
- }
-
- private static class GridRecyclerSpanCountMatcher extends
- BoundedDiagnosingMatcher<View, RecyclerView> {
-
- private final Matcher<Integer> mIntegerMatcher;
-
- private GridRecyclerSpanCountMatcher(Matcher<Integer> integerMatcher) {
- super(RecyclerView.class);
- this.mIntegerMatcher = integerMatcher;
- }
-
- @Override
- protected void describeMoreTo(Description description) {
- description.appendText("RecyclerView grid layout span count to match: ");
- this.mIntegerMatcher.describeTo(description);
- }
-
- @Override
- protected boolean matchesSafely(RecyclerView view, Description mismatchDescription) {
- int spanCount = ((GridLayoutManager) view.getLayoutManager()).getSpanCount();
- if (this.mIntegerMatcher.matches(spanCount)) {
- return true;
- } else {
- mismatchDescription.appendText("RecyclerView grid layout span count was ")
- .appendValue(spanCount);
- return false;
- }
- }
- }
-
- private void givenAppTargets(int appCount) {
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTest(appCount);
- setupResolverControllers(resolvedComponentInfos);
- }
-
- private void updateMaxTargetsPerRowResource(int targetsPerRow) {
- Resources resources = Mockito.spy(
- InstrumentationRegistry.getInstrumentation().getContext().getResources());
- ChooserActivityOverrideData.getInstance().resources = resources;
- doReturn(targetsPerRow).when(resources).getInteger(
- R.integer.config_chooser_max_targets_per_row);
- }
-
- private SparseArray<Pair<ShortcutLoader, Consumer<ShortcutLoader.Result>>>
- createShortcutLoaderFactory() {
- 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;
- };
- return shortcutLoaders;
- }
-
- private static ImageLoader createImageLoader(Uri uri, Bitmap bitmap) {
- return new TestPreviewImageLoader(Collections.singletonMap(uri, bitmap));
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/UnbundledChooserActivityWorkProfileTest.java b/java/tests/src/com/android/intentresolver/v2/UnbundledChooserActivityWorkProfileTest.java
deleted file mode 100644
index e4ec1776..00000000
--- a/java/tests/src/com/android/intentresolver/v2/UnbundledChooserActivityWorkProfileTest.java
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * 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.v2;
-
-import static android.testing.PollingCheck.waitFor;
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.action.ViewActions.swipeUp;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-import static com.android.intentresolver.v2.ChooserWrapperActivity.sOverrides;
-import static com.android.intentresolver.v2.UnbundledChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.NO_BLOCKER;
-import static com.android.intentresolver.v2.UnbundledChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_ACCESS_BLOCKER;
-import static com.android.intentresolver.v2.UnbundledChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_SHARE_BLOCKER;
-import static com.android.intentresolver.v2.UnbundledChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_ACCESS_BLOCKER;
-import static com.android.intentresolver.v2.UnbundledChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_SHARE_BLOCKER;
-import static com.android.intentresolver.v2.UnbundledChooserActivityWorkProfileTest.TestCase.Tab.PERSONAL;
-import static com.android.intentresolver.v2.UnbundledChooserActivityWorkProfileTest.TestCase.Tab.WORK;
-import static org.hamcrest.CoreMatchers.not;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-
-import android.companion.DeviceFilter;
-import android.content.Intent;
-import android.os.UserHandle;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.espresso.NoMatchingViewException;
-import androidx.test.rule.ActivityTestRule;
-
-import com.android.intentresolver.AnnotatedUserHandles;
-import com.android.intentresolver.R;
-import com.android.intentresolver.ResolvedComponentInfo;
-import com.android.intentresolver.ResolverDataProvider;
-import com.android.intentresolver.v2.UnbundledChooserActivityWorkProfileTest.TestCase.Tab;
-
-import junit.framework.AssertionFailedError;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-import dagger.hilt.android.testing.HiltAndroidRule;
-import dagger.hilt.android.testing.HiltAndroidTest;
-
-@DeviceFilter.MediumType
-@RunWith(Parameterized.class)
-@HiltAndroidTest
-public class UnbundledChooserActivityWorkProfileTest {
-
- private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
- .getInstrumentation().getTargetContext().getUser();
- private static final UserHandle WORK_USER_HANDLE = UserHandle.of(10);
-
- @Rule(order = 0)
- public HiltAndroidRule mHiltAndroidRule = new HiltAndroidRule(this);
-
- @Rule(order = 1)
- public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
- new ActivityTestRule<>(ChooserWrapperActivity.class, false,
- false);
- private final TestCase mTestCase;
-
- public UnbundledChooserActivityWorkProfileTest(TestCase testCase) {
- mTestCase = testCase;
- }
-
- @Before
- public void cleanOverrideData() {
- // TODO: use the other form of `adoptShellPermissionIdentity()` where we explicitly list the
- // permissions we require (which we'll read from the manifest at runtime).
- InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .adoptShellPermissionIdentity();
-
- sOverrides.reset();
- }
-
- @Test
- public void testBlocker() {
- setUpPersonalAndWorkComponentInfos();
- sOverrides.hasCrossProfileIntents = mTestCase.hasCrossProfileIntents();
-
- launchActivity(mTestCase.getIsSendAction());
- switchToTab(mTestCase.getTab());
-
- switch (mTestCase.getExpectedBlocker()) {
- case NO_BLOCKER:
- assertNoBlockerDisplayed();
- break;
- case PERSONAL_PROFILE_SHARE_BLOCKER:
- assertCantSharePersonalAppsBlockerDisplayed();
- break;
- case WORK_PROFILE_SHARE_BLOCKER:
- assertCantShareWorkAppsBlockerDisplayed();
- break;
- case PERSONAL_PROFILE_ACCESS_BLOCKER:
- assertCantAccessPersonalAppsBlockerDisplayed();
- break;
- case WORK_PROFILE_ACCESS_BLOCKER:
- assertCantAccessWorkAppsBlockerDisplayed();
- break;
- }
- }
-
- @Parameterized.Parameters(name = "{0}")
- public static Collection tests() {
- return Arrays.asList(
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ WORK_PROFILE_SHARE_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ PERSONAL_PROFILE_SHARE_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ true,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ WORK,
- /* expectedBlocker= */ WORK_PROFILE_ACCESS_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ WORK_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ PERSONAL_PROFILE_ACCESS_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ true,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- ),
- new TestCase(
- /* isSendAction= */ false,
- /* hasCrossProfileIntents= */ false,
- /* myUserHandle= */ PERSONAL_USER_HANDLE,
- /* tab= */ PERSONAL,
- /* expectedBlocker= */ NO_BLOCKER
- )
- );
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
- int numberOfResults, int userId, UserHandle resolvedForUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- infoList.add(
- ResolverDataProvider
- .createResolvedComponentInfoWithOtherId(i, userId, resolvedForUser));
- }
- return infoList;
- }
-
- private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults,
- UserHandle resolvedForUser) {
- List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
- for (int i = 0; i < numberOfResults; i++) {
- infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, resolvedForUser));
- }
- return infoList;
- }
-
- private void setUpPersonalAndWorkComponentInfos() {
- ChooserWrapperActivity.sOverrides.annotatedUserHandles = AnnotatedUserHandles.newBuilder()
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(mTestCase.getMyUserHandle())
- .setPersonalProfileUserHandle(PERSONAL_USER_HANDLE)
- .setWorkProfileUserHandle(WORK_USER_HANDLE)
- .build();
- int workProfileTargets = 4;
- List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3,
- /* userId */ WORK_USER_HANDLE.getIdentifier(), PERSONAL_USER_HANDLE);
- List<ResolvedComponentInfo> workResolvedComponentInfos =
- createResolvedComponentsForTest(workProfileTargets, WORK_USER_HANDLE);
- setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
- }
-
- private void setupResolverControllers(
- List<ResolvedComponentInfo> personalResolvedComponentInfos,
- List<ResolvedComponentInfo> workResolvedComponentInfos) {
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .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() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- private void assertCantAccessWorkAppsBlockerDisplayed() {
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- onView(withText(R.string.resolver_cant_access_work_apps_explanation))
- .check(matches(isDisplayed()));
- }
-
- private void assertCantAccessPersonalAppsBlockerDisplayed() {
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- onView(withText(R.string.resolver_cant_access_personal_apps_explanation))
- .check(matches(isDisplayed()));
- }
-
- private void assertCantShareWorkAppsBlockerDisplayed() {
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- onView(withText(R.string.resolver_cant_share_with_work_apps_explanation))
- .check(matches(isDisplayed()));
- }
-
- private void assertCantSharePersonalAppsBlockerDisplayed() {
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(isDisplayed()));
- onView(withText(R.string.resolver_cant_share_with_personal_apps_explanation))
- .check(matches(isDisplayed()));
- }
-
- private void assertNoBlockerDisplayed() {
- try {
- onView(withText(R.string.resolver_cross_profile_blocked))
- .check(matches(not(isDisplayed())));
- } catch (NoMatchingViewException ignored) {
- }
- }
-
- private void switchToTab(Tab tab) {
- final int stringId = tab == Tab.WORK ? R.string.resolver_work_tab
- : R.string.resolver_personal_tab;
-
- waitFor(() -> {
- onView(withText(stringId)).perform(click());
- waitForIdle();
-
- try {
- onView(withText(stringId)).check(matches(isSelected()));
- return true;
- } catch (AssertionFailedError e) {
- return false;
- }
- });
-
- onView(withId(com.android.internal.R.id.contentPanel))
- .perform(swipeUp());
- waitForIdle();
- }
-
- private Intent createTextIntent(boolean isSendAction) {
- Intent sendIntent = new Intent();
- if (isSendAction) {
- sendIntent.setAction(Intent.ACTION_SEND);
- }
- sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
- sendIntent.setType("text/plain");
- return sendIntent;
- }
-
- private void launchActivity(boolean isSendAction) {
- Intent sendIntent = createTextIntent(isSendAction);
- mActivityRule.launchActivity(Intent.createChooser(sendIntent, "Test"));
- waitForIdle();
- }
-
- public static class TestCase {
- private final boolean mIsSendAction;
- private final boolean mHasCrossProfileIntents;
- private final UserHandle mMyUserHandle;
- private final Tab mTab;
- private final ExpectedBlocker mExpectedBlocker;
-
- public enum ExpectedBlocker {
- NO_BLOCKER,
- PERSONAL_PROFILE_SHARE_BLOCKER,
- WORK_PROFILE_SHARE_BLOCKER,
- PERSONAL_PROFILE_ACCESS_BLOCKER,
- WORK_PROFILE_ACCESS_BLOCKER
- }
-
- public enum Tab {
- WORK,
- PERSONAL
- }
-
- public TestCase(boolean isSendAction, boolean hasCrossProfileIntents,
- UserHandle myUserHandle, Tab tab, ExpectedBlocker expectedBlocker) {
- mIsSendAction = isSendAction;
- mHasCrossProfileIntents = hasCrossProfileIntents;
- mMyUserHandle = myUserHandle;
- mTab = tab;
- mExpectedBlocker = expectedBlocker;
- }
-
- public boolean getIsSendAction() {
- return mIsSendAction;
- }
-
- public boolean hasCrossProfileIntents() {
- return mHasCrossProfileIntents;
- }
-
- public UserHandle getMyUserHandle() {
- return mMyUserHandle;
- }
-
- public Tab getTab() {
- return mTab;
- }
-
- public ExpectedBlocker getExpectedBlocker() {
- return mExpectedBlocker;
- }
-
- @Override
- public String toString() {
- StringBuilder result = new StringBuilder("test");
-
- if (mTab == WORK) {
- result.append("WorkTab_");
- } else {
- result.append("PersonalTab_");
- }
-
- if (mIsSendAction) {
- result.append("sendAction_");
- } else {
- result.append("notSendAction_");
- }
-
- if (mHasCrossProfileIntents) {
- result.append("hasCrossProfileIntents_");
- } else {
- result.append("doesNotHaveCrossProfileIntents_");
- }
-
- if (mMyUserHandle.equals(PERSONAL_USER_HANDLE)) {
- result.append("myUserIsPersonal_");
- } else {
- result.append("myUserIsWork_");
- }
-
- if (mExpectedBlocker == ExpectedBlocker.NO_BLOCKER) {
- result.append("thenNoBlocker");
- } else if (mExpectedBlocker == PERSONAL_PROFILE_ACCESS_BLOCKER) {
- result.append("thenAccessBlockerOnPersonalProfile");
- } else if (mExpectedBlocker == PERSONAL_PROFILE_SHARE_BLOCKER) {
- result.append("thenShareBlockerOnPersonalProfile");
- } else if (mExpectedBlocker == WORK_PROFILE_ACCESS_BLOCKER) {
- result.append("thenAccessBlockerOnWorkProfile");
- } else if (mExpectedBlocker == WORK_PROFILE_SHARE_BLOCKER) {
- result.append("thenShareBlockerOnWorkProfile");
- }
-
- return result.toString();
- }
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/coroutines/Flow.kt b/java/tests/src/com/android/intentresolver/v2/coroutines/Flow.kt
deleted file mode 100644
index a5677d94..00000000
--- a/java/tests/src/com/android/intentresolver/v2/coroutines/Flow.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-@file:Suppress("OPT_IN_USAGE")
-
-package com.android.intentresolver.v2.coroutines
-
-/*
- * 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.
- */
-
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.properties.ReadOnlyProperty
-import kotlin.reflect.KProperty
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-
-/**
- * Collect [flow] in a new [Job] and return a getter for the last collected value.
- *
- * ```
- * fun myTest() = runTest {
- * // ...
- * val actual by collectLastValue(underTest.flow)
- * assertThat(actual).isEqualTo(expected)
- * }
- * ```
- */
-fun <T> TestScope.collectLastValue(
- flow: Flow<T>,
- context: CoroutineContext = EmptyCoroutineContext,
- start: CoroutineStart = CoroutineStart.DEFAULT,
-): FlowValue<T?> {
- val values by
- collectValues(
- flow = flow,
- context = context,
- start = start,
- )
- return FlowValueImpl { values.lastOrNull() }
-}
-
-/**
- * Collect [flow] in a new [Job] and return a getter for the collection of values collected.
- *
- * ```
- * fun myTest() = runTest {
- * // ...
- * val values by collectValues(underTest.flow)
- * assertThat(values).isEqualTo(listOf(expected1, expected2, ...))
- * }
- * ```
- */
-fun <T> TestScope.collectValues(
- flow: Flow<T>,
- context: CoroutineContext = EmptyCoroutineContext,
- start: CoroutineStart = CoroutineStart.DEFAULT,
-): FlowValue<List<T>> {
- val values = mutableListOf<T>()
- backgroundScope.launch(context, start) { flow.collect(values::add) }
- return FlowValueImpl {
- runCurrent()
- values.toList()
- }
-}
-
-/** @see collectLastValue */
-interface FlowValue<T> : ReadOnlyProperty<Any?, T> {
- operator fun invoke(): T
-}
-
-private class FlowValueImpl<T>(private val block: () -> T) : FlowValue<T> {
- override operator fun invoke(): T = block()
- override fun getValue(thisRef: Any?, property: KProperty<*>): T = invoke()
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/data/repository/UserRepositoryImplTest.kt b/java/tests/src/com/android/intentresolver/v2/data/repository/UserRepositoryImplTest.kt
deleted file mode 100644
index 4f514db5..00000000
--- a/java/tests/src/com/android/intentresolver/v2/data/repository/UserRepositoryImplTest.kt
+++ /dev/null
@@ -1,222 +0,0 @@
-package com.android.intentresolver.v2.data.repository
-
-import android.content.Intent
-import android.content.pm.UserInfo
-import android.os.UserHandle
-import android.os.UserHandle.SYSTEM
-import android.os.UserHandle.USER_SYSTEM
-import android.os.UserManager
-import com.android.intentresolver.mock
-import com.android.intentresolver.v2.coroutines.collectLastValue
-import com.android.intentresolver.v2.data.model.User
-import com.android.intentresolver.v2.data.model.User.Role
-import com.android.intentresolver.v2.platform.FakeUserManager
-import com.android.intentresolver.v2.platform.FakeUserManager.ProfileType
-import com.android.intentresolver.whenever
-import com.google.common.truth.Truth.assertThat
-import com.google.common.truth.Truth.assertWithMessage
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.mockito.Mockito
-import org.mockito.Mockito.doReturn
-
-internal class UserRepositoryImplTest {
- private val userManager = FakeUserManager()
- private val userState = userManager.state
-
- @Test
- fun initialization() = runTest {
- val repo = createUserRepository(userManager)
- val users by collectLastValue(repo.users)
-
- assertWithMessage("collectLastValue(repo.users)").that(users).isNotNull()
- assertThat(users)
- .containsExactly(
- userState.primaryUserHandle,
- User(userState.primaryUserHandle.identifier, Role.PERSONAL)
- )
- }
-
- @Test
- fun createProfile() = runTest {
- val repo = createUserRepository(userManager)
- val users by collectLastValue(repo.users)
-
- assertWithMessage("collectLastValue(repo.users)").that(users).isNotNull()
- assertThat(users!!.values.filter { it.role.type == User.Type.PROFILE }).isEmpty()
-
- val profile = userState.createProfile(ProfileType.WORK)
- assertThat(users).containsEntry(profile, User(profile.identifier, Role.WORK))
- }
-
- @Test
- fun removeProfile() = runTest {
- val repo = createUserRepository(userManager)
- val users by collectLastValue(repo.users)
-
- assertWithMessage("collectLastValue(repo.users)").that(users).isNotNull()
- val work = userState.createProfile(ProfileType.WORK)
- assertThat(users).containsEntry(work, User(work.identifier, Role.WORK))
-
- userState.removeProfile(work)
- assertThat(users).doesNotContainEntry(work, User(work.identifier, Role.WORK))
- }
-
- @Test
- fun isAvailable() = runTest {
- val repo = createUserRepository(userManager)
- val work = userState.createProfile(ProfileType.WORK)
-
- val available by collectLastValue(repo.isAvailable(work))
- assertThat(available).isTrue()
-
- userState.setQuietMode(work, true)
- assertThat(available).isFalse()
-
- userState.setQuietMode(work, false)
- assertThat(available).isTrue()
- }
-
- @Test
- fun requestState() = runTest {
- val repo = createUserRepository(userManager)
- val work = userState.createProfile(ProfileType.WORK)
-
- val available by collectLastValue(repo.isAvailable(work))
- assertThat(available).isTrue()
-
- repo.requestState(work, false)
- assertThat(available).isFalse()
-
- repo.requestState(work, true)
- assertThat(available).isTrue()
- }
-
- @Test(expected = IllegalArgumentException::class)
- fun requestState_invalidForFullUser() = runTest {
- val repo = createUserRepository(userManager)
- val primaryUser = User(userState.primaryUserHandle.identifier, Role.PERSONAL)
- repo.requestState(primaryUser, available = false)
- }
-
- /**
- * This and all the 'recovers_from_*' tests below all configure a static event flow instead of
- * using [FakeUserManager]. These tests verify that a invalid broadcast causes the flow to
- * reinitialize with the user profile group.
- */
- @Test
- fun recovers_from_invalid_profile_added_event() = runTest {
- val userManager =
- mockUserManager(validUser = USER_SYSTEM, invalidUser = UserHandle.USER_NULL)
- val events =
- flowOf(
- UserRepositoryImpl.UserEvent(
- Intent.ACTION_PROFILE_ADDED,
- UserHandle.of(UserHandle.USER_NULL)
- )
- )
- val repo =
- UserRepositoryImpl(
- profileParent = SYSTEM,
- userManager = userManager,
- userEvents = events,
- scope = backgroundScope,
- backgroundDispatcher = Dispatchers.Unconfined
- )
- val users by collectLastValue(repo.users)
-
- assertWithMessage("collectLastValue(repo.users)").that(users).isNotNull()
- assertThat(users).containsExactly(SYSTEM, User(USER_SYSTEM, Role.PERSONAL))
- }
-
- @Test
- fun recovers_from_invalid_profile_removed_event() = runTest {
- val userManager =
- mockUserManager(validUser = USER_SYSTEM, invalidUser = UserHandle.USER_NULL)
- val events =
- flowOf(
- UserRepositoryImpl.UserEvent(
- Intent.ACTION_PROFILE_REMOVED,
- UserHandle.of(UserHandle.USER_NULL)
- )
- )
- val repo =
- UserRepositoryImpl(
- profileParent = SYSTEM,
- userManager = userManager,
- userEvents = events,
- scope = backgroundScope,
- backgroundDispatcher = Dispatchers.Unconfined
- )
- val users by collectLastValue(repo.users)
-
- assertWithMessage("collectLastValue(repo.users)").that(users).isNotNull()
- assertThat(users).containsExactly(SYSTEM, User(USER_SYSTEM, Role.PERSONAL))
- }
-
- @Test
- fun recovers_from_invalid_profile_available_event() = runTest {
- val userManager =
- mockUserManager(validUser = USER_SYSTEM, invalidUser = UserHandle.USER_NULL)
- val events =
- flowOf(
- UserRepositoryImpl.UserEvent(
- Intent.ACTION_PROFILE_AVAILABLE,
- UserHandle.of(UserHandle.USER_NULL)
- )
- )
- val repo =
- UserRepositoryImpl(SYSTEM, userManager, events, backgroundScope, Dispatchers.Unconfined)
- val users by collectLastValue(repo.users)
-
- assertWithMessage("collectLastValue(repo.users)").that(users).isNotNull()
- assertThat(users).containsExactly(SYSTEM, User(USER_SYSTEM, Role.PERSONAL))
- }
-
- @Test
- fun recovers_from_unknown_event() = runTest {
- val userManager =
- mockUserManager(validUser = USER_SYSTEM, invalidUser = UserHandle.USER_NULL)
- val events =
- flowOf(
- UserRepositoryImpl.UserEvent("UNKNOWN_EVENT", UserHandle.of(UserHandle.USER_NULL))
- )
- val repo =
- UserRepositoryImpl(
- profileParent = SYSTEM,
- userManager = userManager,
- userEvents = events,
- scope = backgroundScope,
- backgroundDispatcher = Dispatchers.Unconfined
- )
- val users by collectLastValue(repo.users)
-
- assertWithMessage("collectLastValue(repo.users)").that(users).isNotNull()
- assertThat(users).containsExactly(SYSTEM, User(USER_SYSTEM, Role.PERSONAL))
- }
-}
-
-@Suppress("SameParameterValue", "DEPRECATION")
-private fun mockUserManager(validUser: Int, invalidUser: Int) =
- mock<UserManager> {
- val info = UserInfo(validUser, "", "", UserInfo.FLAG_FULL)
- doReturn(listOf(info)).whenever(this).getEnabledProfiles(Mockito.anyInt())
-
- doReturn(info).whenever(this).getUserInfo(Mockito.eq(validUser))
-
- doReturn(listOf<UserInfo>()).whenever(this).getEnabledProfiles(Mockito.eq(invalidUser))
-
- doReturn(null).whenever(this).getUserInfo(Mockito.eq(invalidUser))
- }
-
-private fun TestScope.createUserRepository(userManager: FakeUserManager) =
- UserRepositoryImpl(
- profileParent = userManager.state.primaryUserHandle,
- userManager = userManager,
- userEvents = userManager.state.userEvents,
- scope = backgroundScope,
- backgroundDispatcher = Dispatchers.Unconfined
- )
diff --git a/java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt b/java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt
deleted file mode 100644
index 696dd1fd..00000000
--- a/java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2.emptystate
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import android.widget.TextView
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.intentresolver.any
-import com.android.intentresolver.emptystate.EmptyState
-import com.android.intentresolver.mock
-import com.google.common.truth.Truth.assertThat
-import java.util.Optional
-import java.util.function.Supplier
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-
-class EmptyStateUiHelperTest {
- private val context = InstrumentationRegistry.getInstrumentation().getContext()
-
- var shouldOverrideContainerPadding = false
- val containerPaddingSupplier =
- Supplier<Optional<Int>> {
- Optional.ofNullable(if (shouldOverrideContainerPadding) 42 else null)
- }
-
- lateinit var rootContainer: ViewGroup
- lateinit var mainListView: View // Visible when no empty state is showing.
- lateinit var emptyStateTitleView: TextView
- lateinit var emptyStateSubtitleView: TextView
- lateinit var emptyStateButtonView: View
- lateinit var emptyStateProgressView: View
- lateinit var emptyStateDefaultTextView: View
- lateinit var emptyStateContainerView: View
- lateinit var emptyStateRootView: View
- lateinit var emptyStateUiHelper: EmptyStateUiHelper
-
- @Before
- fun setup() {
- rootContainer = FrameLayout(context)
- LayoutInflater.from(context)
- .inflate(
- com.android.intentresolver.R.layout.resolver_list_per_profile,
- rootContainer,
- true
- )
- mainListView = rootContainer.requireViewById(com.android.internal.R.id.resolver_list)
- emptyStateRootView =
- rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state)
- emptyStateTitleView =
- rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_title)
- emptyStateSubtitleView =
- rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_subtitle)
- emptyStateButtonView =
- rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_button)
- emptyStateProgressView =
- rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_progress)
- emptyStateDefaultTextView = rootContainer.requireViewById(com.android.internal.R.id.empty)
- emptyStateContainerView =
- rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_container)
- emptyStateUiHelper =
- EmptyStateUiHelper(
- rootContainer,
- com.android.internal.R.id.resolver_list,
- containerPaddingSupplier
- )
- }
-
- @Test
- fun testResetViewVisibilities() {
- // First set each view's visibility to differ from the expected "reset" state so we can then
- // assert that they're all reset afterward.
- // TODO: for historic reasons "reset" doesn't cover `emptyStateContainerView`; should it?
- emptyStateRootView.visibility = View.GONE
- emptyStateTitleView.visibility = View.GONE
- emptyStateSubtitleView.visibility = View.GONE
- emptyStateButtonView.visibility = View.VISIBLE
- emptyStateProgressView.visibility = View.VISIBLE
- emptyStateDefaultTextView.visibility = View.VISIBLE
-
- emptyStateUiHelper.resetViewVisibilities()
-
- assertThat(emptyStateRootView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateTitleView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateSubtitleView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateButtonView.visibility).isEqualTo(View.INVISIBLE)
- assertThat(emptyStateProgressView.visibility).isEqualTo(View.GONE)
- assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
- }
-
- @Test
- fun testShowSpinner() {
- emptyStateTitleView.visibility = View.VISIBLE
- emptyStateButtonView.visibility = View.VISIBLE
- emptyStateProgressView.visibility = View.GONE
- emptyStateDefaultTextView.visibility = View.VISIBLE
-
- emptyStateUiHelper.showSpinner()
-
- // TODO: should this cover any other views? Subtitle?
- assertThat(emptyStateTitleView.visibility).isEqualTo(View.INVISIBLE)
- assertThat(emptyStateButtonView.visibility).isEqualTo(View.INVISIBLE)
- assertThat(emptyStateProgressView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
- }
-
- @Test
- fun testHide() {
- emptyStateRootView.visibility = View.VISIBLE
- mainListView.visibility = View.GONE
-
- emptyStateUiHelper.hide()
-
- assertThat(emptyStateRootView.visibility).isEqualTo(View.GONE)
- assertThat(mainListView.visibility).isEqualTo(View.VISIBLE)
- }
-
- @Test
- fun testBottomPaddingDelegate_default() {
- shouldOverrideContainerPadding = false
- emptyStateContainerView.setPadding(1, 2, 3, 4)
-
- emptyStateUiHelper.setupContainerPadding()
-
- assertThat(emptyStateContainerView.paddingLeft).isEqualTo(1)
- assertThat(emptyStateContainerView.paddingTop).isEqualTo(2)
- assertThat(emptyStateContainerView.paddingRight).isEqualTo(3)
- assertThat(emptyStateContainerView.paddingBottom).isEqualTo(4)
- }
-
- @Test
- fun testBottomPaddingDelegate_override() {
- shouldOverrideContainerPadding = true // Set bottom padding to 42.
- emptyStateContainerView.setPadding(1, 2, 3, 4)
-
- emptyStateUiHelper.setupContainerPadding()
-
- assertThat(emptyStateContainerView.paddingLeft).isEqualTo(1)
- assertThat(emptyStateContainerView.paddingTop).isEqualTo(2)
- assertThat(emptyStateContainerView.paddingRight).isEqualTo(3)
- assertThat(emptyStateContainerView.paddingBottom).isEqualTo(42)
- }
-
- @Test
- fun testShowEmptyState_noOnClickHandler() {
- mainListView.visibility = View.VISIBLE
-
- // Note: an `EmptyState.ClickListener` isn't invoked directly by the UI helper; it has to be
- // built into the "on-click handler" that's injected to implement the button-press. We won't
- // display the button without a click "handler," even if it *does* have a `ClickListener`.
- val clickListener = mock<EmptyState.ClickListener>()
-
- val emptyState =
- object : EmptyState {
- override fun getTitle() = "Test title"
- override fun getSubtitle() = "Test subtitle"
-
- override fun getButtonClickListener() = clickListener
- }
- emptyStateUiHelper.showEmptyState(emptyState, null)
-
- assertThat(mainListView.visibility).isEqualTo(View.GONE)
- assertThat(emptyStateRootView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateTitleView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateSubtitleView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateButtonView.visibility).isEqualTo(View.GONE)
- assertThat(emptyStateProgressView.visibility).isEqualTo(View.GONE)
- assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
-
- assertThat(emptyStateTitleView.text).isEqualTo("Test title")
- assertThat(emptyStateSubtitleView.text).isEqualTo("Test subtitle")
-
- verify(clickListener, never()).onClick(any())
- }
-
- @Test
- fun testShowEmptyState_withOnClickHandlerAndClickListener() {
- mainListView.visibility = View.VISIBLE
-
- val clickListener = mock<EmptyState.ClickListener>()
- val onClickHandler = mock<View.OnClickListener>()
-
- val emptyState =
- object : EmptyState {
- override fun getTitle() = "Test title"
- override fun getSubtitle() = "Test subtitle"
-
- override fun getButtonClickListener() = clickListener
- }
- emptyStateUiHelper.showEmptyState(emptyState, onClickHandler)
-
- assertThat(mainListView.visibility).isEqualTo(View.GONE)
- assertThat(emptyStateRootView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateTitleView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateSubtitleView.visibility).isEqualTo(View.VISIBLE)
- assertThat(emptyStateButtonView.visibility).isEqualTo(View.VISIBLE) // Now shown.
- assertThat(emptyStateProgressView.visibility).isEqualTo(View.GONE)
- assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
-
- assertThat(emptyStateTitleView.text).isEqualTo("Test title")
- assertThat(emptyStateSubtitleView.text).isEqualTo("Test subtitle")
-
- emptyStateButtonView.performClick()
-
- verify(onClickHandler).onClick(emptyStateButtonView)
- // The test didn't explicitly configure its `OnClickListener` to relay the click event on
- // to the `EmptyState.ClickListener`, so it still won't have fired here.
- verify(clickListener, never()).onClick(any())
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/ChooserRequestFilteredComponentsTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/ChooserRequestFilteredComponentsTest.kt
deleted file mode 100644
index 59494bed..00000000
--- a/java/tests/src/com/android/intentresolver/v2/listcontroller/ChooserRequestFilteredComponentsTest.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2.listcontroller
-
-import android.content.ComponentName
-import com.android.intentresolver.ChooserRequestParameters
-import com.android.intentresolver.whenever
-import com.google.common.collect.ImmutableList
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-class ChooserRequestFilteredComponentsTest {
-
- @Mock lateinit var mockChooserRequestParameters: ChooserRequestParameters
-
- private lateinit var chooserRequestFilteredComponents: ChooserRequestFilteredComponents
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
-
- chooserRequestFilteredComponents =
- ChooserRequestFilteredComponents(mockChooserRequestParameters)
- }
-
- @Test
- fun isComponentFiltered_returnsRequestParametersFilteredState() {
- // Arrange
- whenever(mockChooserRequestParameters.filteredComponentNames)
- .thenReturn(
- ImmutableList.of(ComponentName("FilteredPackage", "FilteredClass")),
- )
- val testComponent = ComponentName("TestPackage", "TestClass")
- val filteredComponent = ComponentName("FilteredPackage", "FilteredClass")
-
- // Act
- val result = chooserRequestFilteredComponents.isComponentFiltered(testComponent)
- val filteredResult = chooserRequestFilteredComponents.isComponentFiltered(filteredComponent)
-
- // Assert
- assertThat(result).isFalse()
- assertThat(filteredResult).isTrue()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/FakeResolverComparator.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/FakeResolverComparator.kt
deleted file mode 100644
index ce40567e..00000000
--- a/java/tests/src/com/android/intentresolver/v2/listcontroller/FakeResolverComparator.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2.listcontroller
-
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.content.pm.ResolveInfo
-import android.content.res.Configuration
-import android.content.res.Resources
-import android.os.Message
-import android.os.UserHandle
-import com.android.intentresolver.ResolvedComponentInfo
-import com.android.intentresolver.chooser.TargetInfo
-import com.android.intentresolver.model.AbstractResolverComparator
-import com.android.intentresolver.whenever
-import java.util.Locale
-import org.mockito.Mockito
-
-class FakeResolverComparator(
- context: Context =
- Mockito.mock(Context::class.java).also {
- val mockResources = Mockito.mock(Resources::class.java)
- whenever(it.resources).thenReturn(mockResources)
- whenever(mockResources.configuration)
- .thenReturn(Configuration().apply { setLocale(Locale.US) })
- },
- targetIntent: Intent = Intent("TestAction"),
- resolvedActivityUserSpaceList: List<UserHandle> = emptyList(),
- promoteToFirst: ComponentName? = null,
-) :
- AbstractResolverComparator(
- context,
- targetIntent,
- resolvedActivityUserSpaceList,
- promoteToFirst,
- ) {
- var lastUpdateModel: TargetInfo? = null
- private set
- var lastUpdateChooserCounts: Triple<String, UserHandle, String>? = null
- private set
- var destroyCalled = false
- private set
-
- override fun compare(lhs: ResolveInfo?, rhs: ResolveInfo?): Int =
- lhs!!.activityInfo.packageName.compareTo(rhs!!.activityInfo.packageName)
-
- override fun doCompute(targets: MutableList<ResolvedComponentInfo>?) {}
-
- override fun getScore(targetInfo: TargetInfo?): Float = 1.23f
-
- override fun handleResultMessage(message: Message?) {}
-
- override fun updateModel(targetInfo: TargetInfo?) {
- lastUpdateModel = targetInfo
- }
-
- override fun updateChooserCounts(
- packageName: String,
- user: UserHandle,
- action: String,
- ) {
- lastUpdateChooserCounts = Triple(packageName, user, action)
- }
-
- override fun destroy() {
- destroyCalled = true
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/FilterableComponentsTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/FilterableComponentsTest.kt
deleted file mode 100644
index 396505e6..00000000
--- a/java/tests/src/com/android/intentresolver/v2/listcontroller/FilterableComponentsTest.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2.listcontroller
-
-import android.content.ComponentName
-import com.android.intentresolver.ChooserRequestParameters
-import com.android.intentresolver.whenever
-import com.google.common.collect.ImmutableList
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-class FilterableComponentsTest {
-
- @Mock lateinit var mockChooserRequestParameters: ChooserRequestParameters
-
- private val unfilteredComponent = ComponentName("TestPackage", "TestClass")
- private val filteredComponent = ComponentName("FilteredPackage", "FilteredClass")
- private val noComponentFiltering = NoComponentFiltering()
-
- private lateinit var chooserRequestFilteredComponents: ChooserRequestFilteredComponents
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
-
- chooserRequestFilteredComponents =
- ChooserRequestFilteredComponents(mockChooserRequestParameters)
- }
-
- @Test
- fun isComponentFiltered_noComponentFiltering_neverFilters() {
- // Arrange
-
- // Act
- val unfilteredResult = noComponentFiltering.isComponentFiltered(unfilteredComponent)
- val filteredResult = noComponentFiltering.isComponentFiltered(filteredComponent)
-
- // Assert
- assertThat(unfilteredResult).isFalse()
- assertThat(filteredResult).isFalse()
- }
-
- @Test
- fun isComponentFiltered_chooserRequestFilteredComponents_filtersAccordingToChooserRequest() {
- // Arrange
- whenever(mockChooserRequestParameters.filteredComponentNames)
- .thenReturn(
- ImmutableList.of(filteredComponent),
- )
-
- // Act
- val unfilteredResult =
- chooserRequestFilteredComponents.isComponentFiltered(unfilteredComponent)
- val filteredResult = chooserRequestFilteredComponents.isComponentFiltered(filteredComponent)
-
- // Assert
- assertThat(unfilteredResult).isFalse()
- assertThat(filteredResult).isTrue()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/IntentResolverTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/IntentResolverTest.kt
deleted file mode 100644
index 09f6d373..00000000
--- a/java/tests/src/com/android/intentresolver/v2/listcontroller/IntentResolverTest.kt
+++ /dev/null
@@ -1,499 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2.listcontroller
-
-import android.content.ComponentName
-import android.content.Intent
-import android.content.IntentFilter
-import android.content.pm.ActivityInfo
-import android.content.pm.PackageManager
-import android.content.pm.ResolveInfo
-import android.net.Uri
-import android.os.UserHandle
-import com.android.intentresolver.any
-import com.android.intentresolver.eq
-import com.android.intentresolver.kotlinArgumentCaptor
-import com.android.intentresolver.whenever
-import com.google.common.truth.Truth.assertThat
-import java.lang.IndexOutOfBoundsException
-import org.junit.Assert.assertThrows
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mock
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-class IntentResolverTest {
-
- @Mock lateinit var mockPackageManager: PackageManager
-
- private lateinit var intentResolver: IntentResolver
-
- private val fakePinnableComponents =
- object : PinnableComponents {
- override fun isComponentPinned(name: ComponentName): Boolean {
- return name.packageName == "PinnedPackage"
- }
- }
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
-
- intentResolver =
- IntentResolverImpl(mockPackageManager, ResolveListDeduperImpl(fakePinnableComponents))
- }
-
- @Test
- fun getResolversForIntentAsUser_noIntents_returnsEmptyList() {
- // Arrange
- val testIntents = emptyList<Intent>()
-
- // Act
- val result =
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = false,
- shouldGetActivityMetadata = false,
- shouldGetOnlyDefaultActivities = false,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- assertThat(result).isEmpty()
- }
-
- @Test
- fun getResolversForIntentAsUser_noResolveInfo_returnsEmptyList() {
- // Arrange
- val testIntents = listOf(Intent("TestAction"))
- val testResolveInfos = emptyList<ResolveInfo>()
- whenever(mockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), any<UserHandle>()))
- .thenReturn(testResolveInfos)
-
- // Act
- val result =
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = false,
- shouldGetActivityMetadata = false,
- shouldGetOnlyDefaultActivities = false,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- assertThat(result).isEmpty()
- }
-
- @Test
- fun getResolversForIntentAsUser_returnsAllResolveComponentInfo() {
- // Arrange
- val testIntent1 = Intent("TestAction1")
- val testIntent2 = Intent("TestAction2")
- val testIntents = listOf(testIntent1, testIntent2)
- val testResolveInfos1 =
- listOf(
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "TestPackage1"
- activityInfo.name = "TestClass1"
- },
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "TestPackage2"
- activityInfo.name = "TestClass2"
- },
- )
- val testResolveInfos2 =
- listOf(
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "TestPackage3"
- activityInfo.name = "TestClass3"
- },
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "TestPackage4"
- activityInfo.name = "TestClass4"
- },
- )
- whenever(
- mockPackageManager.queryIntentActivitiesAsUser(
- eq(testIntent1),
- anyInt(),
- any<UserHandle>(),
- )
- )
- .thenReturn(testResolveInfos1)
- whenever(
- mockPackageManager.queryIntentActivitiesAsUser(
- eq(testIntent2),
- anyInt(),
- any<UserHandle>(),
- )
- )
- .thenReturn(testResolveInfos2)
-
- // Act
- val result =
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = false,
- shouldGetActivityMetadata = false,
- shouldGetOnlyDefaultActivities = false,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- result.forEachIndexed { index, it ->
- val postfix = index + 1
- assertThat(it.name.packageName).isEqualTo("TestPackage$postfix")
- assertThat(it.name.className).isEqualTo("TestClass$postfix")
- assertThrows(IndexOutOfBoundsException::class.java) { it.getIntentAt(1) }
- }
- assertThat(result.map { it.getIntentAt(0) })
- .containsExactly(
- testIntent1,
- testIntent1,
- testIntent2,
- testIntent2,
- )
- }
-
- @Test
- fun getResolversForIntentAsUser_resolveInfoWithoutUserHandle_isSkipped() {
- // Arrange
- val testIntent = Intent("TestAction")
- val testIntents = listOf(testIntent)
- val testResolveInfos =
- listOf(
- ResolveInfo().apply {
- activityInfo = ActivityInfo()
- activityInfo.packageName = "TestPackage"
- activityInfo.name = "TestClass"
- },
- )
- whenever(
- mockPackageManager.queryIntentActivitiesAsUser(
- any(),
- anyInt(),
- any<UserHandle>(),
- )
- )
- .thenReturn(testResolveInfos)
-
- // Act
- val result =
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = false,
- shouldGetActivityMetadata = false,
- shouldGetOnlyDefaultActivities = false,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- assertThat(result).isEmpty()
- }
-
- @Test
- fun getResolversForIntentAsUser_duplicateComponents_areCombined() {
- // Arrange
- val testIntent1 = Intent("TestAction1")
- val testIntent2 = Intent("TestAction2")
- val testIntents = listOf(testIntent1, testIntent2)
- val testResolveInfos1 =
- listOf(
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "DuplicatePackage"
- activityInfo.name = "DuplicateClass"
- },
- )
- val testResolveInfos2 =
- listOf(
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "DuplicatePackage"
- activityInfo.name = "DuplicateClass"
- },
- )
- whenever(
- mockPackageManager.queryIntentActivitiesAsUser(
- eq(testIntent1),
- anyInt(),
- any<UserHandle>(),
- )
- )
- .thenReturn(testResolveInfos1)
- whenever(
- mockPackageManager.queryIntentActivitiesAsUser(
- eq(testIntent2),
- anyInt(),
- any<UserHandle>(),
- )
- )
- .thenReturn(testResolveInfos2)
-
- // Act
- val result =
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = false,
- shouldGetActivityMetadata = false,
- shouldGetOnlyDefaultActivities = false,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- assertThat(result).hasSize(1)
- with(result.first()) {
- assertThat(name.packageName).isEqualTo("DuplicatePackage")
- assertThat(name.className).isEqualTo("DuplicateClass")
- assertThat(getIntentAt(0)).isEqualTo(testIntent1)
- assertThat(getIntentAt(1)).isEqualTo(testIntent2)
- assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(2) }
- }
- }
-
- @Test
- fun getResolversForIntentAsUser_pinnedComponentsArePinned() {
- // Arrange
- val testIntent1 = Intent("TestAction1")
- val testIntent2 = Intent("TestAction2")
- val testIntents = listOf(testIntent1, testIntent2)
- val testResolveInfos1 =
- listOf(
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "UnpinnedPackage"
- activityInfo.name = "UnpinnedClass"
- },
- )
- val testResolveInfos2 =
- listOf(
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "PinnedPackage"
- activityInfo.name = "PinnedClass"
- },
- )
- whenever(
- mockPackageManager.queryIntentActivitiesAsUser(
- eq(testIntent1),
- anyInt(),
- any<UserHandle>(),
- )
- )
- .thenReturn(testResolveInfos1)
- whenever(
- mockPackageManager.queryIntentActivitiesAsUser(
- eq(testIntent2),
- anyInt(),
- any<UserHandle>(),
- )
- )
- .thenReturn(testResolveInfos2)
-
- // Act
- val result =
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = false,
- shouldGetActivityMetadata = false,
- shouldGetOnlyDefaultActivities = false,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- assertThat(result.map { it.isPinned }).containsExactly(false, true)
- }
-
- @Test
- fun getResolversForIntentAsUser_whenNoExtraBehavior_usesBaseFlags() {
- // Arrange
- val baseFlags =
- PackageManager.MATCH_DIRECT_BOOT_AWARE or
- PackageManager.MATCH_DIRECT_BOOT_UNAWARE or
- PackageManager.MATCH_CLONE_PROFILE
- val testIntent = Intent()
- val testIntents = listOf(testIntent)
-
- // Act
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = false,
- shouldGetActivityMetadata = false,
- shouldGetOnlyDefaultActivities = false,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- val flags = kotlinArgumentCaptor<Int>()
- verify(mockPackageManager)
- .queryIntentActivitiesAsUser(
- any(),
- flags.capture(),
- any<UserHandle>(),
- )
- assertThat(flags.value).isEqualTo(baseFlags)
- }
-
- @Test
- fun getResolversForIntentAsUser_whenShouldGetResolvedFilter_usesGetResolvedFilterFlag() {
- // Arrange
- val testIntent = Intent()
- val testIntents = listOf(testIntent)
-
- // Act
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = true,
- shouldGetActivityMetadata = false,
- shouldGetOnlyDefaultActivities = false,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- val flags = kotlinArgumentCaptor<Int>()
- verify(mockPackageManager)
- .queryIntentActivitiesAsUser(
- any(),
- flags.capture(),
- any<UserHandle>(),
- )
- assertThat(flags.value and PackageManager.GET_RESOLVED_FILTER)
- .isEqualTo(PackageManager.GET_RESOLVED_FILTER)
- }
-
- @Test
- fun getResolversForIntentAsUser_whenShouldGetActivityMetadata_usesGetMetaDataFlag() {
- // Arrange
- val testIntent = Intent()
- val testIntents = listOf(testIntent)
-
- // Act
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = false,
- shouldGetActivityMetadata = true,
- shouldGetOnlyDefaultActivities = false,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- val flags = kotlinArgumentCaptor<Int>()
- verify(mockPackageManager)
- .queryIntentActivitiesAsUser(
- any(),
- flags.capture(),
- any<UserHandle>(),
- )
- assertThat(flags.value and PackageManager.GET_META_DATA)
- .isEqualTo(PackageManager.GET_META_DATA)
- }
-
- @Test
- fun getResolversForIntentAsUser_whenShouldGetOnlyDefaultActivities_usesMatchDefaultOnlyFlag() {
- // Arrange
- val testIntent = Intent()
- val testIntents = listOf(testIntent)
-
- // Act
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = false,
- shouldGetActivityMetadata = false,
- shouldGetOnlyDefaultActivities = true,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- val flags = kotlinArgumentCaptor<Int>()
- verify(mockPackageManager)
- .queryIntentActivitiesAsUser(
- any(),
- flags.capture(),
- any<UserHandle>(),
- )
- assertThat(flags.value and PackageManager.MATCH_DEFAULT_ONLY)
- .isEqualTo(PackageManager.MATCH_DEFAULT_ONLY)
- }
-
- @Test
- fun getResolversForIntentAsUser_whenWebIntent_usesMatchInstantFlag() {
- // Arrange
- val testIntent = Intent(Intent.ACTION_VIEW, Uri.fromParts(IntentFilter.SCHEME_HTTP, "", ""))
- val testIntents = listOf(testIntent)
-
- // Act
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = false,
- shouldGetActivityMetadata = false,
- shouldGetOnlyDefaultActivities = false,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- val flags = kotlinArgumentCaptor<Int>()
- verify(mockPackageManager)
- .queryIntentActivitiesAsUser(
- any(),
- flags.capture(),
- any<UserHandle>(),
- )
- assertThat(flags.value and PackageManager.MATCH_INSTANT)
- .isEqualTo(PackageManager.MATCH_INSTANT)
- }
-
- @Test
- fun getResolversForIntentAsUser_whenActivityMatchExternalFlag_usesMatchInstantFlag() {
- // Arrange
- val testIntent = Intent().addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL)
- val testIntents = listOf(testIntent)
-
- // Act
- intentResolver.getResolversForIntentAsUser(
- shouldGetResolvedFilter = false,
- shouldGetActivityMetadata = false,
- shouldGetOnlyDefaultActivities = false,
- intents = testIntents,
- userHandle = UserHandle(456),
- )
-
- // Assert
- val flags = kotlinArgumentCaptor<Int>()
- verify(mockPackageManager)
- .queryIntentActivitiesAsUser(
- any(),
- flags.capture(),
- any<UserHandle>(),
- )
- assertThat(flags.value and PackageManager.MATCH_INSTANT)
- .isEqualTo(PackageManager.MATCH_INSTANT)
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/LastChosenManagerTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/LastChosenManagerTest.kt
deleted file mode 100644
index ce5e52b1..00000000
--- a/java/tests/src/com/android/intentresolver/v2/listcontroller/LastChosenManagerTest.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2.listcontroller
-
-import android.content.ComponentName
-import android.content.ContentResolver
-import android.content.Intent
-import android.content.IntentFilter
-import android.content.pm.IPackageManager
-import android.content.pm.PackageManager
-import android.content.pm.ResolveInfo
-import com.android.intentresolver.any
-import com.android.intentresolver.eq
-import com.android.intentresolver.nullable
-import com.android.intentresolver.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mock
-import org.mockito.Mockito.isNull
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class LastChosenManagerTest {
-
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
- private val testTargetIntent = Intent("TestAction")
-
- @Mock lateinit var mockContentResolver: ContentResolver
- @Mock lateinit var mockIPackageManager: IPackageManager
-
- private lateinit var lastChosenManager: LastChosenManager
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
-
- lastChosenManager =
- PackageManagerLastChosenManager(mockContentResolver, testDispatcher, testTargetIntent) {
- mockIPackageManager
- }
- }
-
- @Test
- fun getLastChosen_returnsLastChosenActivity() =
- testScope.runTest {
- // Arrange
- val testResolveInfo = ResolveInfo()
- whenever(mockIPackageManager.getLastChosenActivity(any(), nullable(), any()))
- .thenReturn(testResolveInfo)
-
- // Act
- val lastChosen = lastChosenManager.getLastChosen()
- runCurrent()
-
- // Assert
- verify(mockIPackageManager)
- .getLastChosenActivity(
- eq(testTargetIntent),
- isNull(),
- eq(PackageManager.MATCH_DEFAULT_ONLY),
- )
- assertThat(lastChosen).isSameInstanceAs(testResolveInfo)
- }
-
- @Test
- fun setLastChosen_setsLastChosenActivity() =
- testScope.runTest {
- // Arrange
- val testComponent = ComponentName("TestPackage", "TestClass")
- val testIntent = Intent().apply { component = testComponent }
- val testIntentFilter = IntentFilter()
- val testMatch = 456
-
- // Act
- lastChosenManager.setLastChosen(testIntent, testIntentFilter, testMatch)
- runCurrent()
-
- // Assert
- verify(mockIPackageManager)
- .setLastChosenActivity(
- eq(testIntent),
- isNull(),
- eq(PackageManager.MATCH_DEFAULT_ONLY),
- eq(testIntentFilter),
- eq(testMatch),
- eq(testComponent),
- )
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/PinnableComponentsTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/PinnableComponentsTest.kt
deleted file mode 100644
index 112342ad..00000000
--- a/java/tests/src/com/android/intentresolver/v2/listcontroller/PinnableComponentsTest.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2.listcontroller
-
-import android.content.ComponentName
-import android.content.SharedPreferences
-import com.android.intentresolver.any
-import com.android.intentresolver.eq
-import com.android.intentresolver.whenever
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-class PinnableComponentsTest {
-
- @Mock lateinit var mockSharedPreferences: SharedPreferences
-
- private val unpinnedComponent = ComponentName("TestPackage", "TestClass")
- private val pinnedComponent = ComponentName("PinnedPackage", "PinnedClass")
- private val noComponentPinning = NoComponentPinning()
-
- private lateinit var sharedPreferencesPinnedComponents: PinnableComponents
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
-
- sharedPreferencesPinnedComponents = SharedPreferencesPinnedComponents(mockSharedPreferences)
- }
-
- @Test
- fun isComponentPinned_noComponentPinning_neverPins() {
- // Arrange
-
- // Act
- val unpinnedResult = noComponentPinning.isComponentPinned(unpinnedComponent)
- val pinnedResult = noComponentPinning.isComponentPinned(pinnedComponent)
-
- // Assert
- assertThat(unpinnedResult).isFalse()
- assertThat(pinnedResult).isFalse()
- }
-
- @Test
- fun isComponentFiltered_chooserRequestFilteredComponents_filtersAccordingToChooserRequest() {
- // Arrange
- whenever(mockSharedPreferences.getBoolean(eq(pinnedComponent.flattenToString()), any()))
- .thenReturn(true)
-
- // Act
- val unpinnedResult = sharedPreferencesPinnedComponents.isComponentPinned(unpinnedComponent)
- val pinnedResult = sharedPreferencesPinnedComponents.isComponentPinned(pinnedComponent)
-
- // Assert
- assertThat(unpinnedResult).isFalse()
- assertThat(pinnedResult).isTrue()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolveListDeduperTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolveListDeduperTest.kt
deleted file mode 100644
index 26f0199e..00000000
--- a/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolveListDeduperTest.kt
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2.listcontroller
-
-import android.content.ComponentName
-import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.content.pm.ResolveInfo
-import android.os.UserHandle
-import com.android.intentresolver.ResolvedComponentInfo
-import com.google.common.truth.Truth.assertThat
-import java.lang.IndexOutOfBoundsException
-import org.junit.Assert.assertThrows
-import org.junit.Before
-import org.junit.Test
-
-class ResolveListDeduperTest {
-
- private lateinit var resolveListDeduper: ResolveListDeduper
-
- @Before
- fun setup() {
- resolveListDeduper = ResolveListDeduperImpl(NoComponentPinning())
- }
-
- @Test
- fun addResolveListDedupe_addsDifferentComponents() {
- // Arrange
- val testIntent = Intent()
- val testResolveInfo1 =
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "TestPackage1"
- activityInfo.name = "TestClass1"
- }
- val testResolveInfo2 =
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "TestPackage2"
- activityInfo.name = "TestClass2"
- }
- val testResolvedComponentInfo1 =
- ResolvedComponentInfo(
- ComponentName("TestPackage1", "TestClass1"),
- testIntent,
- testResolveInfo1,
- )
- .apply { isPinned = false }
- val listUnderTest = mutableListOf(testResolvedComponentInfo1)
- val listToAdd = listOf(testResolveInfo2)
-
- // Act
- resolveListDeduper.addToResolveListWithDedupe(
- into = listUnderTest,
- intent = testIntent,
- from = listToAdd,
- )
-
- // Assert
- listUnderTest.forEachIndexed { index, it ->
- val postfix = index + 1
- assertThat(it.name.packageName).isEqualTo("TestPackage$postfix")
- assertThat(it.name.className).isEqualTo("TestClass$postfix")
- assertThat(it.getIntentAt(0)).isEqualTo(testIntent)
- assertThrows(IndexOutOfBoundsException::class.java) { it.getIntentAt(1) }
- }
- }
-
- @Test
- fun addResolveListDedupe_combinesDuplicateComponents() {
- // Arrange
- val testIntent = Intent()
- val testResolveInfo1 =
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "DuplicatePackage"
- activityInfo.name = "DuplicateClass"
- }
- val testResolveInfo2 =
- ResolveInfo().apply {
- userHandle = UserHandle(456)
- activityInfo = ActivityInfo()
- activityInfo.packageName = "DuplicatePackage"
- activityInfo.name = "DuplicateClass"
- }
- val testResolvedComponentInfo1 =
- ResolvedComponentInfo(
- ComponentName("DuplicatePackage", "DuplicateClass"),
- testIntent,
- testResolveInfo1,
- )
- .apply { isPinned = false }
- val listUnderTest = mutableListOf(testResolvedComponentInfo1)
- val listToAdd = listOf(testResolveInfo2)
-
- // Act
- resolveListDeduper.addToResolveListWithDedupe(
- into = listUnderTest,
- intent = testIntent,
- from = listToAdd,
- )
-
- // Assert
- assertThat(listUnderTest).containsExactly(testResolvedComponentInfo1)
- assertThat(testResolvedComponentInfo1.getResolveInfoAt(0)).isEqualTo(testResolveInfo1)
- assertThat(testResolvedComponentInfo1.getResolveInfoAt(1)).isEqualTo(testResolveInfo2)
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentFilteringTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentFilteringTest.kt
deleted file mode 100644
index 9786b801..00000000
--- a/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentFilteringTest.kt
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2.listcontroller
-
-import android.content.ComponentName
-import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
-import android.content.pm.ResolveInfo
-import com.android.intentresolver.ResolvedComponentInfo
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertThrows
-import org.junit.Before
-import org.junit.Test
-
-class ResolvedComponentFilteringTest {
-
- private lateinit var resolvedComponentFiltering: ResolvedComponentFiltering
-
- private val fakeFilterableComponents =
- object : FilterableComponents {
- override fun isComponentFiltered(name: ComponentName): Boolean {
- return name.packageName == "FilteredPackage"
- }
- }
-
- private val fakePermissionChecker =
- object : PermissionChecker {
- override suspend fun checkComponentPermission(
- permission: String,
- uid: Int,
- owningUid: Int,
- exported: Boolean
- ): Int {
- return if (permission == "MissingPermission") {
- PackageManager.PERMISSION_DENIED
- } else {
- PackageManager.PERMISSION_GRANTED
- }
- }
- }
-
- @Before
- fun setup() {
- resolvedComponentFiltering =
- ResolvedComponentFilteringImpl(
- launchedFromUid = 123,
- filterableComponents = fakeFilterableComponents,
- permissionChecker = fakePermissionChecker,
- )
- }
-
- @Test
- fun filterIneligibleActivities_returnsListWithoutFilteredComponents() = runTest {
- // Arrange
- val testIntent = Intent("TestAction")
- val testResolveInfo =
- ResolveInfo().apply {
- activityInfo = ActivityInfo()
- activityInfo.packageName = "TestPackage"
- activityInfo.name = "TestClass"
- activityInfo.permission = "TestPermission"
- activityInfo.applicationInfo = ApplicationInfo()
- activityInfo.applicationInfo.uid = 456
- activityInfo.exported = false
- }
- val filteredResolveInfo =
- ResolveInfo().apply {
- activityInfo = ActivityInfo()
- activityInfo.packageName = "FilteredPackage"
- activityInfo.name = "FilteredClass"
- activityInfo.permission = "TestPermission"
- activityInfo.applicationInfo = ApplicationInfo()
- activityInfo.applicationInfo.uid = 456
- activityInfo.exported = false
- }
- val missingPermissionResolveInfo =
- ResolveInfo().apply {
- activityInfo = ActivityInfo()
- activityInfo.packageName = "NoPermissionPackage"
- activityInfo.name = "NoPermissionClass"
- activityInfo.permission = "MissingPermission"
- activityInfo.applicationInfo = ApplicationInfo()
- activityInfo.applicationInfo.uid = 456
- activityInfo.exported = false
- }
- val testInput =
- listOf(
- ResolvedComponentInfo(
- ComponentName("TestPackage", "TestClass"),
- testIntent,
- testResolveInfo,
- ),
- ResolvedComponentInfo(
- ComponentName("FilteredPackage", "FilteredClass"),
- testIntent,
- filteredResolveInfo,
- ),
- ResolvedComponentInfo(
- ComponentName("NoPermissionPackage", "NoPermissionClass"),
- testIntent,
- missingPermissionResolveInfo,
- )
- )
-
- // Act
- val result = resolvedComponentFiltering.filterIneligibleActivities(testInput)
-
- // Assert
- assertThat(result).hasSize(1)
- with(result.first()) {
- assertThat(name.packageName).isEqualTo("TestPackage")
- assertThat(name.className).isEqualTo("TestClass")
- assertThat(getIntentAt(0)).isEqualTo(testIntent)
- assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) }
- assertThat(getResolveInfoAt(0)).isEqualTo(testResolveInfo)
- assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) }
- }
- }
-
- @Test
- fun filterLowPriority_filtersAfterFirstDifferentPriority() {
- // Arrange
- val testIntent = Intent("TestAction")
- val testResolveInfo =
- ResolveInfo().apply {
- priority = 1
- isDefault = true
- }
- val equalResolveInfo =
- ResolveInfo().apply {
- priority = 1
- isDefault = true
- }
- val diffResolveInfo =
- ResolveInfo().apply {
- priority = 2
- isDefault = true
- }
- val testInput =
- listOf(
- ResolvedComponentInfo(
- ComponentName("TestPackage", "TestClass"),
- testIntent,
- testResolveInfo,
- ),
- ResolvedComponentInfo(
- ComponentName("EqualPackage", "EqualClass"),
- testIntent,
- equalResolveInfo,
- ),
- ResolvedComponentInfo(
- ComponentName("DiffPackage", "DiffClass"),
- testIntent,
- diffResolveInfo,
- ),
- )
-
- // Act
- val result = resolvedComponentFiltering.filterLowPriority(testInput)
-
- // Assert
- assertThat(result).hasSize(2)
- with(result.first()) {
- assertThat(name.packageName).isEqualTo("TestPackage")
- assertThat(name.className).isEqualTo("TestClass")
- assertThat(getIntentAt(0)).isEqualTo(testIntent)
- assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) }
- assertThat(getResolveInfoAt(0)).isEqualTo(testResolveInfo)
- assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) }
- }
- with(result[1]) {
- assertThat(name.packageName).isEqualTo("EqualPackage")
- assertThat(name.className).isEqualTo("EqualClass")
- assertThat(getIntentAt(0)).isEqualTo(testIntent)
- assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) }
- assertThat(getResolveInfoAt(0)).isEqualTo(equalResolveInfo)
- assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) }
- }
- }
-
- @Test
- fun filterLowPriority_filtersAfterFirstDifferentDefault() {
- // Arrange
- val testIntent = Intent("TestAction")
- val testResolveInfo =
- ResolveInfo().apply {
- priority = 1
- isDefault = true
- }
- val equalResolveInfo =
- ResolveInfo().apply {
- priority = 1
- isDefault = true
- }
- val diffResolveInfo =
- ResolveInfo().apply {
- priority = 1
- isDefault = false
- }
- val testInput =
- listOf(
- ResolvedComponentInfo(
- ComponentName("TestPackage", "TestClass"),
- testIntent,
- testResolveInfo,
- ),
- ResolvedComponentInfo(
- ComponentName("EqualPackage", "EqualClass"),
- testIntent,
- equalResolveInfo,
- ),
- ResolvedComponentInfo(
- ComponentName("DiffPackage", "DiffClass"),
- testIntent,
- diffResolveInfo,
- ),
- )
-
- // Act
- val result = resolvedComponentFiltering.filterLowPriority(testInput)
-
- // Assert
- assertThat(result).hasSize(2)
- with(result.first()) {
- assertThat(name.packageName).isEqualTo("TestPackage")
- assertThat(name.className).isEqualTo("TestClass")
- assertThat(getIntentAt(0)).isEqualTo(testIntent)
- assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) }
- assertThat(getResolveInfoAt(0)).isEqualTo(testResolveInfo)
- assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) }
- }
- with(result[1]) {
- assertThat(name.packageName).isEqualTo("EqualPackage")
- assertThat(name.className).isEqualTo("EqualClass")
- assertThat(getIntentAt(0)).isEqualTo(testIntent)
- assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) }
- assertThat(getResolveInfoAt(0)).isEqualTo(equalResolveInfo)
- assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) }
- }
- }
-
- @Test
- fun filterLowPriority_whenNoDifference_returnsOriginal() {
- // Arrange
- val testIntent = Intent("TestAction")
- val testResolveInfo =
- ResolveInfo().apply {
- priority = 1
- isDefault = true
- }
- val equalResolveInfo =
- ResolveInfo().apply {
- priority = 1
- isDefault = true
- }
- val testInput =
- listOf(
- ResolvedComponentInfo(
- ComponentName("TestPackage", "TestClass"),
- testIntent,
- testResolveInfo,
- ),
- ResolvedComponentInfo(
- ComponentName("EqualPackage", "EqualClass"),
- testIntent,
- equalResolveInfo,
- ),
- )
-
- // Act
- val result = resolvedComponentFiltering.filterLowPriority(testInput)
-
- // Assert
- assertThat(result).hasSize(2)
- with(result.first()) {
- assertThat(name.packageName).isEqualTo("TestPackage")
- assertThat(name.className).isEqualTo("TestClass")
- assertThat(getIntentAt(0)).isEqualTo(testIntent)
- assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) }
- assertThat(getResolveInfoAt(0)).isEqualTo(testResolveInfo)
- assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) }
- }
- with(result[1]) {
- assertThat(name.packageName).isEqualTo("EqualPackage")
- assertThat(name.className).isEqualTo("EqualClass")
- assertThat(getIntentAt(0)).isEqualTo(testIntent)
- assertThrows(IndexOutOfBoundsException::class.java) { getIntentAt(1) }
- assertThat(getResolveInfoAt(0)).isEqualTo(equalResolveInfo)
- assertThrows(IndexOutOfBoundsException::class.java) { getResolveInfoAt(1) }
- }
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentSortingTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentSortingTest.kt
deleted file mode 100644
index 39b328ee..00000000
--- a/java/tests/src/com/android/intentresolver/v2/listcontroller/ResolvedComponentSortingTest.kt
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2.listcontroller
-
-import android.content.ComponentName
-import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.content.pm.ApplicationInfo
-import android.content.pm.ResolveInfo
-import android.os.UserHandle
-import com.android.intentresolver.ResolvedComponentInfo
-import com.android.intentresolver.chooser.DisplayResolveInfo
-import com.android.intentresolver.chooser.TargetInfo
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.async
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.mockito.Mockito
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class ResolvedComponentSortingTest {
-
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
-
- private val fakeResolverComparator = FakeResolverComparator()
-
- private val resolvedComponentSorting =
- ResolvedComponentSortingImpl(testDispatcher, fakeResolverComparator)
-
- @Test
- fun sorted_onNullList_returnsNull() =
- testScope.runTest {
- // Arrange
- val testInput: List<ResolvedComponentInfo>? = null
-
- // Act
- val result = resolvedComponentSorting.sorted(testInput)
- runCurrent()
-
- // Assert
- assertThat(result).isNull()
- }
-
- @Test
- fun sorted_onEmptyList_returnsEmptyList() =
- testScope.runTest {
- // Arrange
- val testInput = emptyList<ResolvedComponentInfo>()
-
- // Act
- val result = resolvedComponentSorting.sorted(testInput)
- runCurrent()
-
- // Assert
- assertThat(result).isEmpty()
- }
-
- @Test
- fun sorted_returnsListSortedByGivenComparator() =
- testScope.runTest {
- // Arrange
- val testIntent = Intent("TestAction")
- val testInput =
- listOf(
- ResolveInfo().apply {
- activityInfo = ActivityInfo()
- activityInfo.packageName = "TestPackage3"
- activityInfo.name = "TestClass3"
- },
- ResolveInfo().apply {
- activityInfo = ActivityInfo()
- activityInfo.packageName = "TestPackage1"
- activityInfo.name = "TestClass1"
- },
- ResolveInfo().apply {
- activityInfo = ActivityInfo()
- activityInfo.packageName = "TestPackage2"
- activityInfo.name = "TestClass2"
- },
- )
- .map {
- it.targetUserId = UserHandle.USER_CURRENT
- ResolvedComponentInfo(
- ComponentName(it.activityInfo.packageName, it.activityInfo.name),
- testIntent,
- it,
- )
- }
-
- // Act
- val result = async { resolvedComponentSorting.sorted(testInput) }
- runCurrent()
-
- // Assert
- assertThat(result.await()?.map { it.name.packageName })
- .containsExactly("TestPackage1", "TestPackage2", "TestPackage3")
- .inOrder()
- }
-
- @Test
- fun getScore_displayResolveInfo_returnsTheScoreAccordingToTheResolverComparator() {
- // Arrange
- val testTarget =
- DisplayResolveInfo.newDisplayResolveInfo(
- Intent(),
- ResolveInfo().apply {
- activityInfo = ActivityInfo()
- activityInfo.name = "TestClass"
- activityInfo.applicationInfo = ApplicationInfo()
- activityInfo.applicationInfo.packageName = "TestPackage"
- },
- Intent(),
- )
-
- // Act
- val result = resolvedComponentSorting.getScore(testTarget)
-
- // Assert
- assertThat(result).isEqualTo(1.23f)
- }
-
- @Test
- fun getScore_targetInfo_returnsTheScoreAccordingToTheResolverComparator() {
- // Arrange
- val mockTargetInfo = Mockito.mock(TargetInfo::class.java)
-
- // Act
- val result = resolvedComponentSorting.getScore(mockTargetInfo)
-
- // Assert
- assertThat(result).isEqualTo(1.23f)
- }
-
- @Test
- fun updateModel_updatesResolverComparatorModel() =
- testScope.runTest {
- // Arrange
- val mockTargetInfo = Mockito.mock(TargetInfo::class.java)
- assertThat(fakeResolverComparator.lastUpdateModel).isNull()
-
- // Act
- resolvedComponentSorting.updateModel(mockTargetInfo)
- runCurrent()
-
- // Assert
- assertThat(fakeResolverComparator.lastUpdateModel).isSameInstanceAs(mockTargetInfo)
- }
-
- @Test
- fun updateChooserCounts_updatesResolverComparaterChooserCounts() =
- testScope.runTest {
- // Arrange
- val testPackageName = "TestPackage"
- val testUser = UserHandle(456)
- val testAction = "TestAction"
- assertThat(fakeResolverComparator.lastUpdateChooserCounts).isNull()
-
- // Act
- resolvedComponentSorting.updateChooserCounts(testPackageName, testUser, testAction)
- runCurrent()
-
- // Assert
- assertThat(fakeResolverComparator.lastUpdateChooserCounts)
- .isEqualTo(Triple(testPackageName, testUser, testAction))
- }
-
- @Test
- fun destroy_destroysResolverComparator() {
- // Arrange
- assertThat(fakeResolverComparator.destroyCalled).isFalse()
-
- // Act
- resolvedComponentSorting.destroy()
-
- // Assert
- assertThat(fakeResolverComparator.destroyCalled).isTrue()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/listcontroller/SharedPreferencesPinnedComponentsTest.kt b/java/tests/src/com/android/intentresolver/v2/listcontroller/SharedPreferencesPinnedComponentsTest.kt
deleted file mode 100644
index 9d6394fa..00000000
--- a/java/tests/src/com/android/intentresolver/v2/listcontroller/SharedPreferencesPinnedComponentsTest.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.v2.listcontroller
-
-import android.content.ComponentName
-import android.content.SharedPreferences
-import com.android.intentresolver.any
-import com.android.intentresolver.eq
-import com.android.intentresolver.whenever
-import com.google.common.truth.Truth
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-
-class SharedPreferencesPinnedComponentsTest {
-
- @Mock lateinit var mockSharedPreferences: SharedPreferences
-
- private lateinit var sharedPreferencesPinnedComponents: SharedPreferencesPinnedComponents
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
-
- sharedPreferencesPinnedComponents = SharedPreferencesPinnedComponents(mockSharedPreferences)
- }
-
- @Test
- fun isComponentPinned_returnsSavedPinnedState() {
- // Arrange
- val testComponent = ComponentName("TestPackage", "TestClass")
- val pinnedComponent = ComponentName("PinnedPackage", "PinnedClass")
- whenever(mockSharedPreferences.getBoolean(eq(pinnedComponent.flattenToString()), any()))
- .thenReturn(true)
-
- // Act
- val result = sharedPreferencesPinnedComponents.isComponentPinned(testComponent)
- val pinnedResult = sharedPreferencesPinnedComponents.isComponentPinned(pinnedComponent)
-
- // Assert
- Mockito.verify(mockSharedPreferences).getBoolean(eq(testComponent.flattenToString()), any())
- Mockito.verify(mockSharedPreferences)
- .getBoolean(eq(pinnedComponent.flattenToString()), any())
- Truth.assertThat(result).isFalse()
- Truth.assertThat(pinnedResult).isTrue()
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/platform/FakeSecureSettings.kt b/java/tests/src/com/android/intentresolver/v2/platform/FakeSecureSettings.kt
deleted file mode 100644
index 4e279623..00000000
--- a/java/tests/src/com/android/intentresolver/v2/platform/FakeSecureSettings.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.android.intentresolver.v2.platform
-
-/**
- * Creates a SecureSettings instance with predefined values:
- *
- * val settings = fakeSecureSettings {
- * putString("stringValue", "example")
- * putInt("intValue", 42)
- * }
- */
-fun fakeSecureSettings(block: FakeSecureSettings.Builder.() -> Unit): SecureSettings {
- return FakeSecureSettings.Builder().apply(block).build()
-}
-
-/** An in memory implementation of [SecureSettings]. */
-class FakeSecureSettings private constructor(private val map: Map<String, String>) :
- SecureSettings {
-
- override fun getString(name: String): String? = map[name]
- override fun getInt(name: String): Int? = getString(name)?.toIntOrNull()
- override fun getLong(name: String): Long? = getString(name)?.toLongOrNull()
- override fun getFloat(name: String): Float? = getString(name)?.toFloatOrNull()
-
- class Builder {
- private val map = mutableMapOf<String, String>()
-
- fun putString(name: String, value: String) {
- map[name] = value
- }
- fun putInt(name: String, value: Int) {
- map[name] = value.toString()
- }
- fun putLong(name: String, value: Long) {
- map[name] = value.toString()
- }
- fun putFloat(name: String, value: Float) {
- map[name] = value.toString()
- }
-
- fun build(): SecureSettings {
- return FakeSecureSettings(map.toMap())
- }
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/platform/FakeSecureSettingsTest.kt b/java/tests/src/com/android/intentresolver/v2/platform/FakeSecureSettingsTest.kt
deleted file mode 100644
index 04c7093d..00000000
--- a/java/tests/src/com/android/intentresolver/v2/platform/FakeSecureSettingsTest.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.android.intentresolver.v2.platform
-
-import com.google.common.truth.Truth.assertThat
-
-class FakeSecureSettingsTest {
-
- private val secureSettings = fakeSecureSettings {
- putInt(intKey, intVal)
- putString(stringKey, stringVal)
- putFloat(floatKey, floatVal)
- putLong(longKey, longVal)
- }
-
- fun testExpectedValues_returned() {
- assertThat(secureSettings.getInt(intKey)).isEqualTo(intVal)
- assertThat(secureSettings.getString(stringKey)).isEqualTo(stringVal)
- assertThat(secureSettings.getFloat(floatKey)).isEqualTo(floatVal)
- assertThat(secureSettings.getLong(longKey)).isEqualTo(longVal)
- }
-
- fun testUndefinedValues_returnNull() {
- assertThat(secureSettings.getInt("unknown")).isNull()
- assertThat(secureSettings.getString("unknown")).isNull()
- assertThat(secureSettings.getFloat("unknown")).isNull()
- assertThat(secureSettings.getLong("unknown")).isNull()
- }
-
- /**
- * FakeSecureSettings models the real secure settings by storing values in String form. The
- * value is returned if/when it can be parsed from the string value, otherwise null.
- */
- fun testMismatchedTypes() {
- assertThat(secureSettings.getString(intKey)).isEqualTo(intVal.toString())
- assertThat(secureSettings.getString(floatKey)).isEqualTo(floatVal.toString())
- assertThat(secureSettings.getString(longKey)).isEqualTo(longVal.toString())
-
- assertThat(secureSettings.getInt(stringKey)).isNull()
- assertThat(secureSettings.getLong(stringKey)).isNull()
- assertThat(secureSettings.getFloat(stringKey)).isNull()
-
- assertThat(secureSettings.getInt(longKey)).isNull()
- assertThat(secureSettings.getFloat(longKey)).isNull() // TODO: verify Long.MAX > Float.MAX ?
-
- assertThat(secureSettings.getLong(floatKey)).isNull() // TODO: or is Float.MAX > Long.MAX?
- assertThat(secureSettings.getInt(floatKey)).isNull()
- }
-
- companion object Data {
- const val intKey = "int"
- const val intVal = Int.MAX_VALUE
-
- const val stringKey = "string"
- const val stringVal = "String"
-
- const val floatKey = "float"
- const val floatVal = Float.MAX_VALUE
-
- const val longKey = "long"
- const val longVal = Long.MAX_VALUE
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/v2/platform/FakeUserManager.kt b/java/tests/src/com/android/intentresolver/v2/platform/FakeUserManager.kt
deleted file mode 100644
index 370e5a00..00000000
--- a/java/tests/src/com/android/intentresolver/v2/platform/FakeUserManager.kt
+++ /dev/null
@@ -1,239 +0,0 @@
-package com.android.intentresolver.v2.platform
-
-import android.content.Context
-import android.content.Intent.ACTION_MANAGED_PROFILE_AVAILABLE
-import android.content.Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE
-import android.content.Intent.ACTION_PROFILE_ADDED
-import android.content.Intent.ACTION_PROFILE_AVAILABLE
-import android.content.Intent.ACTION_PROFILE_REMOVED
-import android.content.Intent.ACTION_PROFILE_UNAVAILABLE
-import android.content.pm.UserInfo
-import android.content.pm.UserInfo.FLAG_FULL
-import android.content.pm.UserInfo.FLAG_INITIALIZED
-import android.content.pm.UserInfo.FLAG_PROFILE
-import android.content.pm.UserInfo.NO_PROFILE_GROUP_ID
-import android.os.IUserManager
-import android.os.UserHandle
-import android.os.UserManager
-import androidx.annotation.NonNull
-import com.android.intentresolver.THROWS_EXCEPTION
-import com.android.intentresolver.mock
-import com.android.intentresolver.v2.data.repository.UserRepositoryImpl.UserEvent
-import com.android.intentresolver.v2.platform.FakeUserManager.State
-import com.android.intentresolver.whenever
-import kotlin.random.Random
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.consumeAsFlow
-import org.mockito.Mockito.RETURNS_SELF
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.withSettings
-
-/**
- * A stand-in for [UserManager] to support testing of data layer components which depend on it.
- *
- * This fake targets system applications which need to interact with any or all of the current
- * user's associated profiles (as reported by [getEnabledProfiles]). Support for manipulating
- * non-profile (full) secondary users (switching active foreground user, adding or removing users)
- * is not included.
- *
- * Upon creation [FakeUserManager] contains a single primary (full) user with a randomized ID. This
- * is available from [FakeUserManager.state] using [primaryUserHandle][State.primaryUserHandle] or
- * [getPrimaryUser][State.getPrimaryUser].
- *
- * To make state changes, use functions available from [FakeUserManager.state]:
- * * [createProfile][State.createProfile]
- * * [removeProfile][State.removeProfile]
- * * [setQuietMode][State.setQuietMode]
- *
- * Any functionality not explicitly overridden here is guaranteed to throw an exception when
- * accessed (access to the real system service is prevented).
- */
-class FakeUserManager(val state: State = State()) :
- UserManager(/* context = */ mockContext(), /* service = */ mockService()) {
-
- enum class ProfileType {
- WORK,
- CLONE,
- PRIVATE
- }
-
- override fun getProfileParent(userHandle: UserHandle): UserHandle? {
- return state.getUserOrNull(userHandle)?.let { user ->
- if (user.isProfile) {
- state.getUserOrNull(UserHandle.of(user.profileGroupId))?.userHandle
- } else {
- null
- }
- }
- }
-
- override fun getUserInfo(userId: Int): UserInfo? {
- return state.getUserOrNull(UserHandle.of(userId))
- }
-
- @Suppress("OVERRIDE_DEPRECATION")
- override fun getEnabledProfiles(userId: Int): List<UserInfo> {
- val user = state.users.single { it.id == userId }
- return state.users.filter { other ->
- user.id == other.id || user.profileGroupId == other.profileGroupId
- }
- }
-
- override fun requestQuietModeEnabled(
- enableQuietMode: Boolean,
- @NonNull userHandle: UserHandle
- ): Boolean {
- state.setQuietMode(userHandle, enableQuietMode)
- return true
- }
-
- override fun isQuietModeEnabled(userHandle: UserHandle): Boolean {
- return state.getUser(userHandle).isQuietModeEnabled
- }
-
- override fun toString(): String {
- return "FakeUserManager(state=$state)"
- }
-
- class State {
- private val eventChannel = Channel<UserEvent>()
- private val userInfoMap: MutableMap<UserHandle, UserInfo> = mutableMapOf()
-
- /** The id of the primary/full/system user, which is automatically created. */
- val primaryUserHandle: UserHandle
-
- /**
- * Retrieves the primary user. The value returned changes, but the values are immutable.
- *
- * Do not cache this value in tests, between operations.
- */
- fun getPrimaryUser(): UserInfo = getUser(primaryUserHandle)
-
- private var nextUserId: Int = 100 + Random.nextInt(0, 900)
-
- /**
- * A flow of [UserEvent] which emulates those normally generated from system broadcasts.
- *
- * Events are produced by calls to [createPrimaryUser], [createProfile], [removeProfile].
- */
- val userEvents: Flow<UserEvent>
-
- val users: List<UserInfo>
- get() = userInfoMap.values.toList()
-
- val userHandles: List<UserHandle>
- get() = userInfoMap.keys.toList()
-
- init {
- primaryUserHandle = createPrimaryUser(allocateNextId())
- userEvents = eventChannel.consumeAsFlow()
- }
-
- private fun allocateNextId() = nextUserId++
-
- private fun createPrimaryUser(id: Int): UserHandle {
- val userInfo =
- UserInfo(id, "", "", FLAG_INITIALIZED or FLAG_FULL, USER_TYPE_FULL_SYSTEM)
- userInfoMap[userInfo.userHandle] = userInfo
- return userInfo.userHandle
- }
-
- fun getUserOrNull(handle: UserHandle): UserInfo? = userInfoMap[handle]
-
- fun getUser(handle: UserHandle): UserInfo =
- requireNotNull(getUserOrNull(handle)) {
- "Expected userInfoMap to contain an entry for $handle"
- }
-
- fun setQuietMode(user: UserHandle, quietMode: Boolean) {
- userInfoMap[user]?.also {
- it.flags =
- if (quietMode) {
- it.flags or UserInfo.FLAG_QUIET_MODE
- } else {
- it.flags and UserInfo.FLAG_QUIET_MODE.inv()
- }
- val actions = mutableListOf<String>()
- if (quietMode) {
- actions += ACTION_PROFILE_UNAVAILABLE
- if (it.isManagedProfile) {
- actions += ACTION_MANAGED_PROFILE_UNAVAILABLE
- }
- } else {
- actions += ACTION_PROFILE_AVAILABLE
- if (it.isManagedProfile) {
- actions += ACTION_MANAGED_PROFILE_AVAILABLE
- }
- }
- actions.forEach { action ->
- eventChannel.trySend(UserEvent(action, user, quietMode))
- }
- }
- }
-
- fun createProfile(type: ProfileType, parent: UserHandle = primaryUserHandle): UserHandle {
- val parentUser = getUser(parent)
- require(!parentUser.isProfile) { "Parent user cannot be a profile" }
-
- // Ensure the parent user has a valid profileGroupId
- if (parentUser.profileGroupId == NO_PROFILE_GROUP_ID) {
- parentUser.profileGroupId = parentUser.id
- }
- val id = allocateNextId()
- val userInfo =
- UserInfo(id, "", "", FLAG_INITIALIZED or FLAG_PROFILE, type.toUserType()).apply {
- profileGroupId = parentUser.profileGroupId
- }
- userInfoMap[userInfo.userHandle] = userInfo
- eventChannel.trySend(UserEvent(ACTION_PROFILE_ADDED, userInfo.userHandle))
- return userInfo.userHandle
- }
-
- fun removeProfile(handle: UserHandle): Boolean {
- return userInfoMap[handle]?.let { user ->
- require(user.isProfile) { "Only profiles can be removed" }
- userInfoMap.remove(user.userHandle)
- eventChannel.trySend(UserEvent(ACTION_PROFILE_REMOVED, user.userHandle))
- return true
- }
- ?: false
- }
-
- override fun toString() = buildString {
- append("State(nextUserId=$nextUserId, userInfoMap=[")
- userInfoMap.entries.forEach {
- append("UserHandle[${it.key.identifier}] = ${it.value.debugString},")
- }
- append("])")
- }
- }
-}
-
-/** A safe mock of [Context] which throws on any unstubbed method call. */
-private fun mockContext(user: UserHandle = UserHandle.SYSTEM): Context {
- return mock<Context>(withSettings().defaultAnswer(THROWS_EXCEPTION)) {
- doAnswer(RETURNS_SELF).whenever(this).applicationContext
- doReturn(user).whenever(this).user
- doReturn(user.identifier).whenever(this).userId
- }
-}
-
-private fun FakeUserManager.ProfileType.toUserType(): String {
- return when (this) {
- FakeUserManager.ProfileType.WORK -> UserManager.USER_TYPE_PROFILE_MANAGED
- FakeUserManager.ProfileType.CLONE -> UserManager.USER_TYPE_PROFILE_CLONE
- FakeUserManager.ProfileType.PRIVATE -> UserManager.USER_TYPE_PROFILE_PRIVATE
- }
-}
-
-/** A safe mock of [IUserManager] which throws on any unstubbed method call. */
-fun mockService(): IUserManager {
- return mock<IUserManager>(withSettings().defaultAnswer(THROWS_EXCEPTION))
-}
-
-val UserInfo.debugString: String
- get() =
- "UserInfo(id=$id, profileGroupId=$profileGroupId, name=$name, " +
- "type=$userType, flags=${UserInfo.flagsToString(flags)})"
diff --git a/java/tests/src/com/android/intentresolver/v2/platform/FakeUserManagerTest.kt b/java/tests/src/com/android/intentresolver/v2/platform/FakeUserManagerTest.kt
deleted file mode 100644
index a2239192..00000000
--- a/java/tests/src/com/android/intentresolver/v2/platform/FakeUserManagerTest.kt
+++ /dev/null
@@ -1,128 +0,0 @@
-package com.android.intentresolver.v2.platform
-
-import android.content.pm.UserInfo
-import android.content.pm.UserInfo.NO_PROFILE_GROUP_ID
-import android.os.UserHandle
-import android.os.UserManager
-import com.android.intentresolver.v2.platform.FakeUserManager.ProfileType
-import com.google.common.truth.Correspondence
-import com.google.common.truth.Truth.assertThat
-import com.google.common.truth.Truth.assertWithMessage
-import org.junit.Assert.assertTrue
-import org.junit.Test
-
-class FakeUserManagerTest {
- private val userManager = FakeUserManager()
- private val state = userManager.state
-
- @Test
- fun initialState() {
- val personal = userManager.getEnabledProfiles(state.primaryUserHandle.identifier).single()
-
- assertThat(personal.id).isEqualTo(state.primaryUserHandle.identifier)
- assertThat(personal.userType).isEqualTo(UserManager.USER_TYPE_FULL_SYSTEM)
- assertThat(personal.flags and UserInfo.FLAG_FULL).isEqualTo(UserInfo.FLAG_FULL)
- }
-
- @Test
- fun getProfileParent() {
- val workHandle = state.createProfile(ProfileType.WORK)
-
- assertThat(userManager.getProfileParent(state.primaryUserHandle)).isNull()
- assertThat(userManager.getProfileParent(workHandle)).isEqualTo(state.primaryUserHandle)
- assertThat(userManager.getProfileParent(UserHandle.of(-1))).isNull()
- }
-
- @Test
- fun getUserInfo() {
- val personalUser =
- requireNotNull(userManager.getUserInfo(state.primaryUserHandle.identifier)) {
- "Expected getUserInfo to return non-null"
- }
- assertTrue(userInfoAreEqual.apply(personalUser, state.getPrimaryUser()))
-
- val workHandle = state.createProfile(ProfileType.WORK)
-
- val workUser =
- requireNotNull(userManager.getUserInfo(workHandle.identifier)) {
- "Expected getUserInfo to return non-null"
- }
- assertTrue(
- userInfoAreEqual.apply(workUser, userManager.getUserInfo(workHandle.identifier)!!)
- )
- }
-
- @Test
- fun getEnabledProfiles_usingParentId() {
- val personal = state.primaryUserHandle
- val work = state.createProfile(ProfileType.WORK)
- val private = state.createProfile(ProfileType.PRIVATE)
-
- val enabledProfiles = userManager.getEnabledProfiles(personal.identifier)
-
- assertWithMessage("enabledProfiles: List<UserInfo>")
- .that(enabledProfiles)
- .comparingElementsUsing(userInfoEquality)
- .displayingDiffsPairedBy { it.id }
- .containsExactly(state.getPrimaryUser(), state.getUser(work), state.getUser(private))
- }
-
- @Test
- fun getEnabledProfiles_usingProfileId() {
- val clone = state.createProfile(ProfileType.CLONE)
-
- val enabledProfiles = userManager.getEnabledProfiles(clone.identifier)
-
- assertWithMessage("getEnabledProfiles(clone.identifier)")
- .that(enabledProfiles)
- .comparingElementsUsing(userInfoEquality)
- .displayingDiffsPairedBy { it.id }
- .containsExactly(state.getPrimaryUser(), state.getUser(clone))
- }
-
- @Test
- fun getUserOrNull() {
- val personal = state.getPrimaryUser()
-
- assertThat(state.getUserOrNull(personal.userHandle)).isEqualTo(personal)
- assertThat(state.getUserOrNull(UserHandle.of(personal.id - 1))).isNull()
- }
-
- @Test
- fun createProfile() {
- // Order dependent: profile creation modifies the primary user
- val workHandle = state.createProfile(ProfileType.WORK)
-
- val primaryUser = state.getPrimaryUser()
- val workUser = state.getUser(workHandle)
-
- assertThat(primaryUser.profileGroupId).isNotEqualTo(NO_PROFILE_GROUP_ID)
- assertThat(workUser.profileGroupId).isEqualTo(primaryUser.profileGroupId)
- }
-
- @Test
- fun removeProfile() {
- val personal = state.getPrimaryUser()
- val work = state.createProfile(ProfileType.WORK)
- val private = state.createProfile(ProfileType.PRIVATE)
-
- state.removeProfile(private)
- assertThat(state.userHandles).containsExactly(personal.userHandle, work)
- }
-
- @Test(expected = IllegalArgumentException::class)
- fun removeProfile_primaryNotAllowed() {
- state.removeProfile(state.primaryUserHandle)
- }
-}
-
-private val userInfoAreEqual =
- Correspondence.BinaryPredicate<UserInfo, UserInfo> { actual, expected ->
- actual.id == expected.id &&
- actual.profileGroupId == expected.profileGroupId &&
- actual.userType == expected.userType &&
- actual.flags == expected.flags
- }
-
-val userInfoEquality: Correspondence<UserInfo, UserInfo> =
- Correspondence.from(userInfoAreEqual, "==")
diff --git a/java/tests/src/com/android/intentresolver/v2/platform/NearbyShareModuleTest.kt b/java/tests/src/com/android/intentresolver/v2/platform/NearbyShareModuleTest.kt
deleted file mode 100644
index fd5c8b3f..00000000
--- a/java/tests/src/com/android/intentresolver/v2/platform/NearbyShareModuleTest.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-package com.android.intentresolver.v2.platform
-
-import android.content.ComponentName
-import android.content.Context
-import android.content.res.Configuration
-import android.provider.Settings
-import android.testing.TestableResources
-
-import androidx.test.platform.app.InstrumentationRegistry
-
-import com.android.intentresolver.R
-
-import com.google.common.truth.Truth8.assertThat
-
-import org.junit.Before
-import org.junit.Test
-
-class NearbyShareModuleTest {
-
- lateinit var context: Context
-
- /** Create Resources with overridden values. */
- private fun Context.fakeResources(
- config: Configuration? = null,
- block: TestableResources.() -> Unit
- ) =
- TestableResources(resources)
- .apply { config?.let { overrideConfiguration(it) } }
- .apply(block)
- .resources
-
- @Before
- fun setup() {
- val instr = InstrumentationRegistry.getInstrumentation()
- context = instr.context
- }
-
- @Test
- fun valueIsAbsent_whenUnset() {
- val secureSettings = fakeSecureSettings {}
- val resources =
- context.fakeResources { addOverride(R.string.config_defaultNearbySharingComponent, "") }
-
- val componentName = NearbyShareModule.nearbyShareComponent(resources, secureSettings)
- assertThat(componentName).isEmpty()
- }
-
- @Test
- fun defaultValue_readFromResources() {
- val secureSettings = fakeSecureSettings {}
- val resources =
- context.fakeResources {
- addOverride(
- R.string.config_defaultNearbySharingComponent,
- "com.example/.ComponentName"
- )
- }
-
- val nearbyShareComponent = NearbyShareModule.nearbyShareComponent(resources, secureSettings)
-
- assertThat(nearbyShareComponent).hasValue(
- ComponentName.unflattenFromString("com.example/.ComponentName"))
- }
-
- @Test
- fun secureSettings_overridesDefault() {
- val secureSettings = fakeSecureSettings {
- putString(Settings.Secure.NEARBY_SHARING_COMPONENT, "com.example/.BComponent")
- }
- val resources =
- context.fakeResources {
- addOverride(
- R.string.config_defaultNearbySharingComponent,
- "com.example/.AComponent"
- )
- }
-
- val nearbyShareComponent = NearbyShareModule.nearbyShareComponent(resources, secureSettings)
-
- assertThat(nearbyShareComponent).hasValue(
- ComponentName.unflattenFromString("com.example/.BComponent"))
- }
-}
diff --git a/java/tests/src/com/android/intentresolver/widget/BatchPreviewLoaderTest.kt b/java/tests/src/com/android/intentresolver/widget/BatchPreviewLoaderTest.kt
deleted file mode 100644
index 4f4223c0..00000000
--- a/java/tests/src/com/android/intentresolver/widget/BatchPreviewLoaderTest.kt
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.widget
-
-import android.graphics.Bitmap
-import android.net.Uri
-import com.android.intentresolver.captureMany
-import com.android.intentresolver.mock
-import com.android.intentresolver.widget.ScrollableImagePreviewView.BatchPreviewLoader
-import com.android.intentresolver.widget.ScrollableImagePreviewView.Preview
-import com.android.intentresolver.widget.ScrollableImagePreviewView.PreviewType
-import com.android.intentresolver.withArgCaptor
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.asFlow
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-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.atLeast
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class BatchPreviewLoaderTest {
- private val dispatcher = UnconfinedTestDispatcher()
- private val testScope = CoroutineScope(dispatcher)
- private val onCompletion = mock<() -> Unit>()
- private val onUpdate = mock<(List<Preview>) -> Unit>()
-
- @Before
- fun setup() {
- Dispatchers.setMain(dispatcher)
- }
-
- @After
- fun cleanup() {
- testScope.cancel()
- Dispatchers.resetMain()
- }
-
- @Test
- fun test_allImagesWithinViewPort_oneUpdate() {
- val imageLoader = TestImageLoader(testScope)
- val uriOne = createUri(1)
- val uriTwo = createUri(2)
- imageLoader.setUriLoadingOrder(succeed(uriTwo), succeed(uriOne))
- val testSubject =
- BatchPreviewLoader(
- imageLoader,
- previews(uriOne, uriTwo),
- totalItemCount = 2,
- onUpdate,
- onCompletion
- )
- testSubject.loadAspectRatios(200) { _, _, _ -> 100 }
- dispatcher.scheduler.advanceUntilIdle()
-
- verify(onCompletion, times(1)).invoke()
- val list = withArgCaptor { verify(onUpdate, times(1)).invoke(capture()) }.map { it.uri }
- assertThat(list).containsExactly(uriOne, uriTwo).inOrder()
- }
-
- @Test
- fun test_allImagesWithinViewPortOneFailed_failedPreviewIsNotUpdated() {
- val imageLoader = TestImageLoader(testScope)
- val uriOne = createUri(1)
- val uriTwo = createUri(2)
- val uriThree = createUri(3)
- imageLoader.setUriLoadingOrder(succeed(uriThree), fail(uriTwo), succeed(uriOne))
- val testSubject =
- BatchPreviewLoader(
- imageLoader,
- previews(uriOne, uriTwo, uriThree),
- totalItemCount = 3,
- onUpdate,
- onCompletion
- )
- testSubject.loadAspectRatios(200) { _, _, _ -> 100 }
- dispatcher.scheduler.advanceUntilIdle()
-
- verify(onCompletion, times(1)).invoke()
- val list = withArgCaptor { verify(onUpdate, times(1)).invoke(capture()) }.map { it.uri }
- assertThat(list).containsExactly(uriOne, uriThree).inOrder()
- }
-
- @Test
- fun test_imagesLoadedNotInOrder_updatedInOrder() {
- val imageLoader = TestImageLoader(testScope)
- val uris = Array(10) { createUri(it) }
- val loadingOrder =
- Array(uris.size) { i ->
- val uriIdx =
- when {
- i % 2 == 1 -> i - 1
- i % 2 == 0 && i < uris.size - 1 -> i + 1
- else -> i
- }
- succeed(uris[uriIdx])
- }
- imageLoader.setUriLoadingOrder(*loadingOrder)
- val testSubject =
- BatchPreviewLoader(imageLoader, previews(*uris), uris.size, onUpdate, onCompletion)
- testSubject.loadAspectRatios(200) { _, _, _ -> 100 }
- dispatcher.scheduler.advanceUntilIdle()
-
- verify(onCompletion, times(1)).invoke()
- val list =
- captureMany { verify(onUpdate, atLeast(1)).invoke(capture()) }
- .fold(ArrayList<Preview>()) { acc, update -> acc.apply { addAll(update) } }
- .map { it.uri }
- assertThat(list).containsExactly(*uris).inOrder()
- }
-
- @Test
- fun test_imagesLoadedNotInOrderSomeFailed_updatedInOrder() {
- val imageLoader = TestImageLoader(testScope)
- val uris = Array(10) { createUri(it) }
- val loadingOrder =
- Array(uris.size) { i ->
- val uriIdx =
- when {
- i % 2 == 1 -> i - 1
- i % 2 == 0 && i < uris.size - 1 -> i + 1
- else -> i
- }
- if (uriIdx % 2 == 0) fail(uris[uriIdx]) else succeed(uris[uriIdx])
- }
- val expectedUris = Array(uris.size / 2) { createUri(it * 2 + 1) }
- imageLoader.setUriLoadingOrder(*loadingOrder)
- val testSubject =
- BatchPreviewLoader(imageLoader, previews(*uris), uris.size, onUpdate, onCompletion)
- testSubject.loadAspectRatios(200) { _, _, _ -> 100 }
- dispatcher.scheduler.advanceUntilIdle()
-
- verify(onCompletion, times(1)).invoke()
- val list =
- captureMany { verify(onUpdate, atLeast(1)).invoke(capture()) }
- .fold(ArrayList<Preview>()) { acc, update -> acc.apply { addAll(update) } }
- .map { it.uri }
- assertThat(list).containsExactly(*expectedUris).inOrder()
- }
-
- private fun createUri(idx: Int): Uri = Uri.parse("content://org.pkg.app/image-$idx.png")
-
- private fun fail(uri: Uri) = uri to false
- private fun succeed(uri: Uri) = uri to true
- private fun previews(vararg uris: Uri) =
- uris
- .fold(ArrayList<Preview>(uris.size)) { acc, uri ->
- acc.apply { add(Preview(PreviewType.Image, uri, editAction = null)) }
- }
- .asFlow()
-}
-
-private class TestImageLoader(scope: CoroutineScope) : suspend (Uri, Boolean) -> Bitmap? {
- private val loadingOrder = ArrayDeque<Pair<Uri, Boolean>>()
- private val pendingRequests = LinkedHashMap<Uri, CompletableDeferred<Bitmap?>>()
- private val flow = MutableSharedFlow<Unit>(replay = 1)
- private val bitmap by lazy { Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888) }
-
- init {
- scope.launch {
- flow.collect {
- while (true) {
- val (nextUri, isLoaded) = loadingOrder.firstOrNull() ?: break
- val deferred = pendingRequests.remove(nextUri) ?: break
- loadingOrder.removeFirst()
- deferred.complete(if (isLoaded) bitmap else null)
- }
- if (loadingOrder.isEmpty()) {
- pendingRequests.forEach { (uri, deferred) -> deferred.complete(bitmap) }
- pendingRequests.clear()
- }
- }
- }
- }
-
- fun setUriLoadingOrder(vararg uris: Pair<Uri, Boolean>) {
- loadingOrder.clear()
- loadingOrder.addAll(uris)
- }
-
- override suspend fun invoke(uri: Uri, cache: Boolean): Bitmap? {
- val deferred = pendingRequests.getOrPut(uri) { CompletableDeferred() }
- flow.tryEmit(Unit)
- return deferred.await()
- }
-}