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