diff options
| -rw-r--r-- | java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java | 15 | ||||
| -rw-r--r-- | tests/unit/src/com/android/intentresolver/chooser/TargetInfoTest.kt | 430 |
2 files changed, 251 insertions, 194 deletions
diff --git a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java index b97e6b45..4fe28384 100644 --- a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java +++ b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java @@ -17,9 +17,11 @@ package com.android.intentresolver.chooser; import android.app.Activity; +import android.content.ComponentName; import android.content.Intent; import android.os.Bundle; import android.os.UserHandle; +import android.util.Log; import androidx.annotation.Nullable; @@ -121,6 +123,19 @@ public class MultiDisplayResolveInfo extends DisplayResolveInfo { } @Override + public ComponentName getResolvedComponentName() { + if (hasSelected()) { + return mTargetInfos.get(mSelected).getResolvedComponentName(); + } + // It is not expected to have this method be called on an unselected multi-display item. + // Call super to preserve the legacy (most likely erroneous) behavior. + Log.wtf( + "ChooserActivity", + "retrieving ResolvedComponentName from an unselected MultiDisplayResolveInfo"); + return super.getResolvedComponentName(); + } + + @Override public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { return mTargetInfos.get(mSelected).startAsUser(activity, options, user); } diff --git a/tests/unit/src/com/android/intentresolver/chooser/TargetInfoTest.kt b/tests/unit/src/com/android/intentresolver/chooser/TargetInfoTest.kt index a7574c12..b346bee5 100644 --- a/tests/unit/src/com/android/intentresolver/chooser/TargetInfoTest.kt +++ b/tests/unit/src/com/android/intentresolver/chooser/TargetInfoTest.kt @@ -24,9 +24,10 @@ 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.annotation.UiThreadTest import androidx.test.platform.app.InstrumentationRegistry import com.android.intentresolver.ResolverDataProvider +import com.android.intentresolver.ResolverDataProvider.createResolveInfo import com.android.intentresolver.createChooserTarget import com.android.intentresolver.createShortcutInfo import com.android.intentresolver.mock @@ -41,38 +42,37 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify class TargetInfoTest { - private val PERSONAL_USER_HANDLE: UserHandle = InstrumentationRegistry - .getInstrumentation().getTargetContext().getUser() + 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() + InstrumentationRegistry.getInstrumentation() + .uiAutomation .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.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. + @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.isChooserTargetInfo).isTrue() // From legacy inheritance model. assertThat(info.hasDisplayIcon()).isTrue() assertThat(info.displayIconHolder.displayIcon) - .isInstanceOf(AnimatedVectorDrawable::class.java) + .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 @@ -82,34 +82,43 @@ class TargetInfoTest { @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 baseDisplayInfo = + DisplayResolveInfo.newDisplayResolveInfo( + resolvedIntent, + 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(), - ) + 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.isChooserTargetInfo).isTrue() // From legacy inheritance model. assertThat(targetInfo.displayResolveInfo).isSameInstanceAs(baseDisplayInfo) assertThat(targetInfo.chooserTargetComponentName).isEqualTo(chooserTarget.componentName) assertThat(targetInfo.directShareShortcutId).isEqualTo(shortcutInfo.id) @@ -121,33 +130,43 @@ class TargetInfoTest { @Test fun test_SelectableTargetInfo_componentName_no_source_info() { - val chooserTarget = createChooserTarget( - "title", 0.3f, ResolverDataProvider.createComponentName(1), "test_shortcut_id") + 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 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 backupResolveInfo = + ResolveInfo().apply { + activityInfo = + ActivityInfo().apply { + packageName = pkgName + name = className + } } - } - - val targetInfo = SelectableTargetInfo.newSelectableTargetInfo( - null, - backupResolveInfo, - mock(), - chooserTarget, - 0.1f, - shortcutInfo, - appTarget, - mock(), - ) + + val targetInfo = + SelectableTargetInfo.newSelectableTargetInfo( + null, + backupResolveInfo, + mock(), + chooserTarget, + 0.1f, + shortcutInfo, + appTarget, + mock(), + ) assertThat(targetInfo.resolvedComponentName).isEqualTo(ComponentName(pkgName, className)) } @@ -156,32 +175,41 @@ class TargetInfoTest { 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 baseDisplayInfo = + DisplayResolveInfo.newDisplayResolveInfo( + resolvedIntent, + 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 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() @@ -193,18 +221,19 @@ class TargetInfoTest { 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() + val resolveInfo = 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 @@ -218,31 +247,30 @@ class TargetInfoTest { val extraMatch = Intent("REFINE_ME") extraMatch.putExtra("extraMatch", true) - val originalInfo = DisplayResolveInfo.newDisplayResolveInfo( - originalIntent, - ResolverDataProvider.createResolveInfo(3, 0), - "label", - "extended info", - originalIntent - ) + val originalInfo = + DisplayResolveInfo.newDisplayResolveInfo( + originalIntent, + 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` + 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() + 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)) + assertThat(refinedResult.resolvedIntent?.getBooleanExtra("originalIntent", false)).isFalse() + assertThat(refinedResult.resolvedIntent?.getBooleanExtra("mismatchedAlternate", false)) .isFalse() - assertThat(refinedResult?.resolvedIntent?.getBooleanExtra("extraMatch", false)).isFalse() + assertThat(refinedResult.resolvedIntent?.getBooleanExtra("extraMatch", false)).isFalse() } @Test @@ -252,13 +280,14 @@ class TargetInfoTest { val mismatchedAlternate = Intent("DOESNT_MATCH") mismatchedAlternate.putExtra("mismatchedAlternate", true) - val originalInfo = DisplayResolveInfo.newDisplayResolveInfo( - originalIntent, - ResolverDataProvider.createResolveInfo(3, 0), - "label", - "extended info", - originalIntent - ) + val originalInfo = + DisplayResolveInfo.newDisplayResolveInfo( + originalIntent, + createResolveInfo(3, 0), + "label", + "extended info", + originalIntent + ) originalInfo.addAlternateSourceIntent(mismatchedAlternate) val refinement = Intent("PROPOSED_REFINEMENT") @@ -271,41 +300,50 @@ class TargetInfoTest { 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) + val packageName = "org.pkg.app" + val componentA = ComponentName(packageName, "org.pkg.app.ActivityA") + val componentB = ComponentName(packageName, "org.pkg.app.ActivityB") + val resolveInfoA = createResolveInfo(componentA, 0, PERSONAL_USER_HANDLE) + val resolveInfoB = createResolveInfo(componentB, 0, PERSONAL_USER_HANDLE) + val firstTargetInfo = + DisplayResolveInfo.newDisplayResolveInfo( + intent, + resolveInfoA, + "label 1", + "extended info 1", + intent + ) + val secondTargetInfo = + DisplayResolveInfo.newDisplayResolveInfo( + intent, + resolveInfoB, + "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.extendedInfo).isNull() + + assertThat(multiTargetInfo.allDisplayTargets) + .containsExactly(firstTargetInfo, secondTargetInfo) assertThat(multiTargetInfo.hasSelected()).isFalse() - assertThat(multiTargetInfo.getSelectedTarget()).isNull() + assertThat(multiTargetInfo.selectedTarget).isNull() multiTargetInfo.setSelected(1) assertThat(multiTargetInfo.hasSelected()).isTrue() - assertThat(multiTargetInfo.getSelectedTarget()).isEqualTo(secondTargetInfo) + assertThat(multiTargetInfo.selectedTarget).isEqualTo(secondTargetInfo) + assertThat(multiTargetInfo.resolvedComponentName).isEqualTo(componentB) val refined = multiTargetInfo.tryToCloneWithAppliedRefinement(intent) assertThat(refined).isInstanceOf(MultiDisplayResolveInfo::class.java) @@ -321,37 +359,40 @@ class TargetInfoTest { 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) - ) + val resolveInfo = 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) @@ -370,22 +411,23 @@ class TargetInfoTest { 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 targetOne = + spy( + DisplayResolveInfo.newDisplayResolveInfo( + sendImage, + createResolveInfo(1, 0), + "Target One", + "Target One", + sendImage + ) ) - ) - val targetTwo = mock<DisplayResolveInfo> { - whenever(tryToCloneWithAppliedRefinement(any())).thenReturn(this) - } - - val multiTargetInfo = MultiDisplayResolveInfo.newMultiDisplayResolveInfo( - listOf(targetOne, targetTwo) - ) + val targetTwo = + mock<DisplayResolveInfo> { + whenever(tryToCloneWithAppliedRefinement(any())).thenReturn(this) + } + + val multiTargetInfo = + MultiDisplayResolveInfo.newMultiDisplayResolveInfo(listOf(targetOne, targetTwo)) multiTargetInfo.setSelected(1) assertThat(multiTargetInfo.selectedTarget).isEqualTo(targetTwo) |