Snap for 9593491 from 4df530f5e4b42341a0dfc945244b73356a4031ff to tm-qpr3-release
Change-Id: I9506333858e547cc4bca5930e5815d991413d270
diff --git a/java/res/layout/chooser_grid_preview_file.xml b/java/res/layout/chooser_grid_preview_file.xml
index 9475511..095e5d6 100644
--- a/java/res/layout/chooser_grid_preview_file.xml
+++ b/java/res/layout/chooser_grid_preview_file.xml
@@ -65,7 +65,8 @@
android:ellipsize="middle"
android:gravity="start|top"
android:paddingRight="24dp"
- android:singleLine="true"/>
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.ChooserDefault" />
</LinearLayout>
<TextView
diff --git a/java/res/layout/chooser_grid_preview_image.xml b/java/res/layout/chooser_grid_preview_image.xml
index 614d9b5..792b7d4 100644
--- a/java/res/layout/chooser_grid_preview_image.xml
+++ b/java/res/layout/chooser_grid_preview_image.xml
@@ -55,7 +55,8 @@
android:maxLines="6"
android:ellipsize="end"
android:linksClickable="false"
- android:visibility="gone" />
+ android:visibility="gone"
+ android:textAppearance="@style/TextAppearance.ChooserDefault" />
</LinearLayout>
<TextView
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 229512f..ba6418a 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -47,9 +47,11 @@
<item name="*android:iconfactoryBadgeSize">@dimen/chooser_badge_size</item>
</style>
- <style name="ReselectionAction">
+ <style name="TextAppearance.ChooserDefault"
+ parent="@android:style/TextAppearance.DeviceDefault" />
+
+ <style name="ReselectionAction" parent="TextAppearance.ChooserDefault">
<item name="android:paddingTop">5dp</item>
<item name="android:paddingBottom">5dp</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
</resources>
diff --git a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java
index 4bbf59d..0bbd690 100644
--- a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java
+++ b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java
@@ -27,7 +27,6 @@
import android.os.Bundle;
import android.os.UserHandle;
-import com.android.intentresolver.ResolverActivity;
import com.android.intentresolver.TargetPresentationGetter;
import java.util.ArrayList;
@@ -205,13 +204,7 @@
}
@Override
- public boolean start(Activity activity, Bundle options) {
- activity.startActivity(mResolvedIntent, options);
- return true;
- }
-
- @Override
- public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
+ public boolean startAsCaller(Activity activity, Bundle options, int userId) {
TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
return true;
@@ -220,6 +213,12 @@
@Override
public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
+ // TODO: is this equivalent to `startActivityAsCaller` with `ignoreTargetSecurity=true`? If
+ // so, we can consolidate on the one API method to show that this flag is the only
+ // distinction between `startAsCaller` and `startAsUser`. We can even bake that flag into
+ // the `TargetActivityStarter` upfront since it just reflects our "safe forwarding mode" --
+ // which is constant for the duration of our lifecycle, leaving clients no other
+ // responsibilities in this logic.
activity.startActivityAsUser(mResolvedIntent, options, user);
return false;
}
diff --git a/java/src/com/android/intentresolver/chooser/ImmutableTargetInfo.java b/java/src/com/android/intentresolver/chooser/ImmutableTargetInfo.java
index 315cea4..38991c7 100644
--- a/java/src/com/android/intentresolver/chooser/ImmutableTargetInfo.java
+++ b/java/src/com/android/intentresolver/chooser/ImmutableTargetInfo.java
@@ -31,8 +31,6 @@
import androidx.annotation.VisibleForTesting;
-import com.android.intentresolver.ResolverActivity;
-
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -55,15 +53,6 @@
/** Delegate interface to request that the target be launched by a particular API. */
public interface TargetActivityStarter {
/**
- * Request that the delegate use the {@link Activity#startActivity()} API to launch the
- * specified {@code target}.
- *
- * @return true if the target was launched successfully.
- */
- boolean start(TargetInfo target, Activity activity, Bundle options);
-
-
- /**
* Request that the delegate use the {@link Activity#startAsCaller()} API to launch the
* specified {@code target}.
*
@@ -418,12 +407,7 @@
}
@Override
- public boolean start(Activity activity, Bundle options) {
- return mActivityStarter.start(this, activity, options);
- }
-
- @Override
- public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
+ public boolean startAsCaller(Activity activity, Bundle options, int userId) {
return mActivityStarter.startAsCaller(this, activity, options, userId);
}
diff --git a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java
index 0d79e5d..0938c55 100644
--- a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java
+++ b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java
@@ -21,8 +21,6 @@
import android.os.Bundle;
import android.os.UserHandle;
-import com.android.intentresolver.ResolverActivity;
-
import java.util.ArrayList;
import java.util.List;
@@ -106,12 +104,7 @@
}
@Override
- public boolean start(Activity activity, Bundle options) {
- return mTargetInfos.get(mSelected).start(activity, options);
- }
-
- @Override
- public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
+ public boolean startAsCaller(Activity activity, Bundle options, int userId) {
return mTargetInfos.get(mSelected).startAsCaller(activity, options, userId);
}
diff --git a/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java b/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java
index 9a2c971..6444e13 100644
--- a/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java
+++ b/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java
@@ -18,33 +18,28 @@
import android.annotation.Nullable;
import android.app.Activity;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import com.android.intentresolver.R;
-import com.android.intentresolver.ResolverActivity;
-import java.util.List;
+import java.util.function.Supplier;
/**
* Distinguish between targets that selectable by the user, vs those that are
* placeholders for the system while information is loading in an async manner.
*/
-public abstract class NotSelectableTargetInfo extends ChooserTargetInfo {
+public final class NotSelectableTargetInfo {
/** Create a non-selectable {@link TargetInfo} with no content. */
public static TargetInfo newEmptyTargetInfo() {
- return new NotSelectableTargetInfo() {
- @Override
- public boolean isEmptyTargetInfo() {
- return true;
- }
- };
+ return ImmutableTargetInfo.newBuilder()
+ .setLegacyType(ImmutableTargetInfo.LegacyTargetType.EMPTY_TARGET_INFO)
+ .setDisplayIconHolder(makeReadOnlyIconHolder(() -> null))
+ .setActivityStarter(makeNoOpActivityStarter())
+ .build();
}
/**
@@ -52,108 +47,51 @@
* unless/until it can be replaced by the result of a pending asynchronous load.
*/
public static TargetInfo newPlaceHolderTargetInfo(Context context) {
- return new NotSelectableTargetInfo() {
- @Override
- public boolean isPlaceHolderTargetInfo() {
- return true;
- }
-
- @Override
- public IconHolder getDisplayIconHolder() {
- return new IconHolder() {
- @Override
- public Drawable getDisplayIcon() {
- AnimatedVectorDrawable avd = (AnimatedVectorDrawable)
- context.getDrawable(
- R.drawable.chooser_direct_share_icon_placeholder);
- avd.start(); // Start animation after generation.
- return avd;
- }
-
- @Override
- public void setDisplayIcon(Drawable icon) {}
- };
- }
-
- @Override
- public boolean hasDisplayIcon() {
- return true;
- }
- };
+ return ImmutableTargetInfo.newBuilder()
+ .setLegacyType(ImmutableTargetInfo.LegacyTargetType.PLACEHOLDER_TARGET_INFO)
+ .setDisplayIconHolder(
+ makeReadOnlyIconHolder(() -> makeStartedPlaceholderDrawable(context)))
+ .setActivityStarter(makeNoOpActivityStarter())
+ .build();
}
- public final boolean isNotSelectableTargetInfo() {
- return true;
+ private static Drawable makeStartedPlaceholderDrawable(Context context) {
+ AnimatedVectorDrawable avd = (AnimatedVectorDrawable) context.getDrawable(
+ R.drawable.chooser_direct_share_icon_placeholder);
+ avd.start(); // Start animation after generation.
+ return avd;
}
- public Intent getResolvedIntent() {
- return null;
- }
-
- public ComponentName getResolvedComponentName() {
- return null;
- }
-
- public boolean start(Activity activity, Bundle options) {
- return false;
- }
-
- public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
- return false;
- }
-
- @Nullable
- @Override
- public Intent getTargetIntent() {
- return null;
- }
-
- public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
- return false;
- }
-
- public ResolveInfo getResolveInfo() {
- return null;
- }
-
- public CharSequence getDisplayLabel() {
- return null;
- }
-
- public CharSequence getExtendedInfo() {
- return null;
- }
-
- public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
- return null;
- }
-
- public List<Intent> getAllSourceIntents() {
- return null;
- }
-
- public float getModifiedScore() {
- return -0.1f;
- }
-
- public boolean isSuspended() {
- return false;
- }
-
- public boolean isPinned() {
- return false;
- }
-
- @Override
- public IconHolder getDisplayIconHolder() {
- return new IconHolder() {
+ private static ImmutableTargetInfo.IconHolder makeReadOnlyIconHolder(
+ Supplier</* @Nullable */ Drawable> iconProvider) {
+ return new ImmutableTargetInfo.IconHolder() {
@Override
+ @Nullable
public Drawable getDisplayIcon() {
- return null;
+ return iconProvider.get();
}
@Override
public void setDisplayIcon(Drawable icon) {}
};
}
+
+ private static ImmutableTargetInfo.TargetActivityStarter makeNoOpActivityStarter() {
+ return new ImmutableTargetInfo.TargetActivityStarter() {
+ @Override
+ public boolean startAsCaller(
+ TargetInfo target, Activity activity, Bundle options, int userId) {
+ return false;
+ }
+
+ @Override
+ public boolean startAsUser(
+ TargetInfo target, Activity activity, Bundle options, UserHandle user) {
+ return false;
+ }
+ };
+ }
+
+ // TODO: merge all the APIs up to a single `TargetInfo` class.
+ private NotSelectableTargetInfo() {}
}
diff --git a/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java b/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java
index ca77823..df27c2b 100644
--- a/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java
+++ b/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java
@@ -33,7 +33,6 @@
import android.util.HashedStringCache;
import android.util.Log;
-import com.android.intentresolver.ResolverActivity;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import java.util.ArrayList;
@@ -332,12 +331,7 @@
}
@Override
- public boolean start(Activity activity, Bundle options) {
- return mActivityStarter.start(activity, options);
- }
-
- @Override
- public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
+ public boolean startAsCaller(Activity activity, Bundle options, int userId) {
return mActivityStarter.startAsCaller(activity, options, userId);
}
diff --git a/java/src/com/android/intentresolver/chooser/TargetInfo.java b/java/src/com/android/intentresolver/chooser/TargetInfo.java
index 7dcf66b..69f58a7 100644
--- a/java/src/com/android/intentresolver/chooser/TargetInfo.java
+++ b/java/src/com/android/intentresolver/chooser/TargetInfo.java
@@ -32,8 +32,6 @@
import android.text.TextUtils;
import android.util.HashedStringCache;
-import com.android.intentresolver.ResolverActivity;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -124,24 +122,15 @@
}
/**
- * Start the activity referenced by this target.
- *
- * @param activity calling Activity performing the launch
- * @param options ActivityOptions bundle
- * @return true if the start completed successfully
- */
- boolean start(Activity activity, Bundle options);
-
- /**
- * Start the activity referenced by this target as if the ResolverActivity's caller
- * was performing the start operation.
+ * Start the activity referenced by this target as if the Activity's caller was performing the
+ * start operation.
*
* @param activity calling Activity (actually) performing the launch
* @param options ActivityOptions bundle
* @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller
* @return true if the start completed successfully
*/
- boolean startAsCaller(ResolverActivity activity, Bundle options, int userId);
+ boolean startAsCaller(Activity activity, Bundle options, int userId);
/**
* Start the activity referenced by this target as a given user.
diff --git a/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt b/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt
index 4d825f6..4989a3f 100644
--- a/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt
+++ b/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt
@@ -349,42 +349,8 @@
}
@Test
- fun testActivityStarter_correctNumberOfInvocations_start() {
- val activityStarter = object : TestActivityStarter() {
- override fun startAsCaller(
- target: TargetInfo, activity: Activity, options: Bundle, userId: Int): Boolean {
- throw RuntimeException("Wrong API used: startAsCaller")
- }
-
- 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: Activity = mock()
- val options = Bundle()
- options.putInt("TEST_KEY", 1)
-
- info.start(activity, options)
-
- assertThat(activityStarter.totalInvocations).isEqualTo(1)
- assertThat(activityStarter.lastInvocationTargetInfo).isEqualTo(info)
- assertThat(activityStarter.lastInvocationActivity).isEqualTo(activity)
- assertThat(activityStarter.lastInvocationOptions).isEqualTo(options)
- assertThat(activityStarter.lastInvocationUserId).isNull()
- assertThat(activityStarter.lastInvocationAsCaller).isFalse()
- }
-
- @Test
fun testActivityStarter_correctNumberOfInvocations_startAsCaller() {
val activityStarter = object : TestActivityStarter() {
- override fun start(target: TargetInfo, activity: Activity, options: Bundle): Boolean {
- throw RuntimeException("Wrong API used: start")
- }
-
override fun startAsUser(
target: TargetInfo, activity: Activity, options: Bundle, user: UserHandle
): Boolean {
@@ -410,10 +376,6 @@
@Test
fun testActivityStarter_correctNumberOfInvocations_startAsUser() {
val activityStarter = object : TestActivityStarter() {
- override fun start(target: TargetInfo, activity: Activity, options: Bundle): Boolean {
- throw RuntimeException("Wrong API used: start")
- }
-
override fun startAsCaller(
target: TargetInfo, activity: Activity, options: Bundle, userId: Int): Boolean {
throw RuntimeException("Wrong API used: startAsCaller")
@@ -441,16 +403,14 @@
val info1 = ImmutableTargetInfo.newBuilder().setActivityStarter(activityStarter).build()
val info2 = info1.toBuilder().build()
- info1.start(mock(), Bundle())
+ info1.startAsCaller(mock(), Bundle(), 42)
assertThat(activityStarter.lastInvocationTargetInfo).isEqualTo(info1)
- info2.start(mock(), Bundle())
- assertThat(activityStarter.lastInvocationTargetInfo).isEqualTo(info2)
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(4) // Instance is still shared.
+ assertThat(activityStarter.totalInvocations).isEqualTo(3) // Instance is still shared.
}
}
@@ -462,16 +422,6 @@
var lastInvocationUserId: Integer? = null
var lastInvocationAsCaller = false
- override fun start(target: TargetInfo, activity: Activity, options: Bundle): Boolean {
- ++totalInvocations
- lastInvocationTargetInfo = target
- lastInvocationActivity = activity
- lastInvocationOptions = options
- lastInvocationUserId = null
- lastInvocationAsCaller = false
- return true
- }
-
override fun startAsCaller(
target: TargetInfo, activity: Activity, options: Bundle, userId: Int): Boolean {
++totalInvocations
diff --git a/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt b/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt
index 69948cc..e9dbe00 100644
--- a/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt
+++ b/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt
@@ -22,18 +22,30 @@
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.createChooserTarget
import com.android.intentresolver.createShortcutInfo
import com.android.intentresolver.mock
import com.android.intentresolver.ResolverDataProvider
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
class TargetInfoTest {
private val context = InstrumentationRegistry.getInstrumentation().getContext()
+ @Before
+ fun setup() {
+ // SelectableTargetInfo reads DeviceConfig and needs a permission for that.
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity("android.permission.READ_DEVICE_CONFIG")
+ }
+
@Test
fun testNewEmptyTargetInfo() {
val info = NotSelectableTargetInfo.newEmptyTargetInfo()
@@ -43,13 +55,19 @@
assertThat(info.getDisplayIconHolder().getDisplayIcon()).isNull()
}
+ @UiThreadTest // AnimatedVectorDrawable needs to start from a thread with a Looper.
@Test
fun testNewPlaceholderTargetInfo() {
val info = NotSelectableTargetInfo.newPlaceHolderTargetInfo(context)
- assertThat(info.isPlaceHolderTargetInfo()).isTrue()
- assertThat(info.isChooserTargetInfo()).isTrue() // From legacy inheritance model.
+ assertThat(info.isPlaceHolderTargetInfo).isTrue()
+ assertThat(info.isChooserTargetInfo).isTrue() // From legacy inheritance model.
assertThat(info.hasDisplayIcon()).isTrue()
- // TODO: test infrastructure isn't set up to assert anything about the icon itself.
+ assertThat(info.displayIconHolder.displayIcon)
+ .isInstanceOf(AnimatedVectorDrawable::class.java)
+ // TODO: assert that the animation is pre-started/running (IIUC this requires synchronizing
+ // with some "render thread" per the `AnimatedVectorDrawable` docs). I believe this is
+ // possible using `AnimatorTestRule` but I couldn't find any sample usage in Kotlin nor get
+ // it working myself.
}
@Test