Add simple reselection action implementation

Add a simple reselection action implementation under a compile-time
flag with minimalistic UI -- a row above the action chips with a centerd
text label.

Bug: 262276456
Test: maual test that the feature is disabled on a regualr build
Test: Rebuild the app with the flag enabled an use test applications
to verify the functionality.

Change-Id: I5d7c617524e7d20cc7f4bbdace118eacb9134c19
diff --git a/java/res/layout/chooser_grid_preview_file.xml b/java/res/layout/chooser_grid_preview_file.xml
index e98c327..9475511 100644
--- a/java/res/layout/chooser_grid_preview_file.xml
+++ b/java/res/layout/chooser_grid_preview_file.xml
@@ -68,6 +68,15 @@
         android:singleLine="true"/>
   </LinearLayout>
 
+  <TextView
+      android:id="@+id/reselection_action"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:visibility="gone"
+      android:text="@string/select_files"
+      android:gravity="center"
+      style="@style/ReselectionAction" />
+
   <ViewStub
       android:id="@+id/action_row_stub"
       android:layout_width="match_parent"
diff --git a/java/res/layout/chooser_grid_preview_image.xml b/java/res/layout/chooser_grid_preview_image.xml
index 95cee0c..23bc25d 100644
--- a/java/res/layout/chooser_grid_preview_image.xml
+++ b/java/res/layout/chooser_grid_preview_image.xml
@@ -33,6 +33,15 @@
       android:paddingBottom="@dimen/chooser_view_spacing"
       android:background="?android:attr/colorBackground" />
 
+  <TextView
+      android:id="@+id/reselection_action"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:visibility="gone"
+      android:text="@string/select_images"
+      android:gravity="center"
+      style="@style/ReselectionAction" />
+
   <ViewStub
       android:id="@+id/action_row_stub"
       android:layout_width="match_parent"
diff --git a/java/res/layout/chooser_grid_preview_text.xml b/java/res/layout/chooser_grid_preview_text.xml
index db7282e..49a2edf 100644
--- a/java/res/layout/chooser_grid_preview_text.xml
+++ b/java/res/layout/chooser_grid_preview_text.xml
@@ -52,6 +52,15 @@
 
   </RelativeLayout>
 
+  <TextView
+      android:id="@+id/reselection_action"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:visibility="gone"
+      android:text="@string/select_text"
+      android:gravity="center"
+      style="@style/ReselectionAction" />
+
   <ViewStub
       android:id="@+id/action_row_stub"
       android:layout_width="match_parent"
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index a536d3b..5917950 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -101,4 +101,11 @@
     <string name="miniresolver_use_personal_browser">Use personal browser</string>
     <!-- Button option. Open the link in the work browser. [CHAR LIMIT=NONE] -->
     <string name="miniresolver_use_work_browser">Use work browser</string>
+
+    <!-- Tittle for a button. Launches client-provided content reselection action. -->
+    <string name="select_files">Select Files</string>
+    <!-- Tittle for a button. Launches client-provided content reselection action. -->
+    <string name="select_images">Select Images</string>
+    <!-- Tittle for a button. Launches client-provided content reselection action. -->
+    <string name="select_text">Select Text</string>
 </resources>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index cbbf406..229512f 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -46,4 +46,10 @@
         <item name="*android:iconfactoryIconSize">@dimen/chooser_icon_size</item>
         <item name="*android:iconfactoryBadgeSize">@dimen/chooser_badge_size</item>
     </style>
+
+    <style name="ReselectionAction">
+        <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/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java
index dce8bde..5a1d2a3 100644
--- a/java/src/com/android/intentresolver/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/ChooserActivity.java
@@ -160,6 +160,7 @@
 
     private static final boolean DEBUG = true;
     static final boolean ENABLE_CUSTOM_ACTIONS = false;
+    static final boolean ENABLE_RESELECTION_ACTION = false;
 
     public static final String LAUNCH_LOCATION_DIRECT_SHARE = "direct_share";
     private static final String SHORTCUT_TARGET = "shortcut_target";
@@ -267,7 +268,11 @@
 
         try {
             mChooserRequest = new ChooserRequestParameters(
-                    getIntent(), getReferrer(), getNearbySharingComponent(), ENABLE_CUSTOM_ACTIONS);
+                    getIntent(),
+                    getReferrer(),
+                    getNearbySharingComponent(),
+                    ENABLE_CUSTOM_ACTIONS,
+                    ENABLE_RESELECTION_ACTION);
         } catch (IllegalArgumentException e) {
             Log.e(TAG, "Caller provided invalid Chooser request parameters", e);
             finish();
@@ -743,6 +748,18 @@
                         }
                         return actions;
                     }
+
+                    @Nullable
+                    @Override
+                    public Runnable getReselectionAction() {
+                        if (!ENABLE_RESELECTION_ACTION) {
+                            return null;
+                        }
+                        PendingIntent reselectionAction = mChooserRequest.getReselectionAction();
+                        return reselectionAction == null
+                                ? null
+                                : createReselectionRunnable(reselectionAction);
+                    }
                 };
 
         ViewGroup layout = ChooserContentPreviewUi.displayContentPreview(
@@ -962,6 +979,19 @@
         );
     }
 
+    private Runnable createReselectionRunnable(PendingIntent pendingIntent) {
+        return () -> {
+            try {
+                pendingIntent.send();
+            } catch (PendingIntent.CanceledException e) {
+                Log.d(TAG, "Payload reselection action has been cancelled");
+            }
+            // TODO: add reporting
+            setResult(RESULT_OK);
+            finish();
+        };
+    }
+
     @Nullable
     private View getFirstVisibleImgPreviewView() {
         View firstImage = findViewById(com.android.internal.R.id.content_preview_image_1_large);
diff --git a/java/src/com/android/intentresolver/ChooserContentPreviewUi.java b/java/src/com/android/intentresolver/ChooserContentPreviewUi.java
index 27645da..390c47c 100644
--- a/java/src/com/android/intentresolver/ChooserContentPreviewUi.java
+++ b/java/src/com/android/intentresolver/ChooserContentPreviewUi.java
@@ -90,6 +90,12 @@
 
         /** Create custom actions */
         List<ActionRow.Action> createCustomActions();
+
+        /**
+         * Provides a re-selection action, if any.
+         */
+        @Nullable
+        Runnable getReselectionAction();
     }
 
     /**
@@ -218,6 +224,15 @@
             default:
                 Log.e(TAG, "Unexpected content preview type: " + previewType);
         }
+        Runnable reselectionAction = actionFactory.getReselectionAction();
+        if (reselectionAction != null && layout != null
+                && ChooserActivity.ENABLE_RESELECTION_ACTION) {
+            View reselectionView = layout.findViewById(R.id.reselection_action);
+            if (reselectionView != null) {
+                reselectionView.setVisibility(View.VISIBLE);
+                reselectionView.setOnClickListener(view -> reselectionAction.run());
+            }
+        }
 
         return layout;
     }
diff --git a/java/src/com/android/intentresolver/ChooserRequestParameters.java b/java/src/com/android/intentresolver/ChooserRequestParameters.java
index a7e543a..97bee82 100644
--- a/java/src/com/android/intentresolver/ChooserRequestParameters.java
+++ b/java/src/com/android/intentresolver/ChooserRequestParameters.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -72,6 +73,7 @@
     private final ImmutableList<ComponentName> mFilteredComponentNames;
     private final ImmutableList<ChooserTarget> mCallerChooserTargets;
     private final ImmutableList<ChooserAction> mChooserActions;
+    private final PendingIntent mReselectionAction;
     private final boolean mRetainInOnStop;
 
     @Nullable
@@ -99,7 +101,8 @@
             final Intent clientIntent,
             final Uri referrer,
             @Nullable final ComponentName nearbySharingComponent,
-            boolean extractCustomActions) {
+            boolean extractCustomActions,
+            boolean extractReslectionAction) {
         final Intent requestedTarget = parseTargetIntentExtra(
                 clientIntent.getParcelableExtra(Intent.EXTRA_INTENT));
         mTarget = intentWithModifiedLaunchFlags(requestedTarget);
@@ -137,6 +140,9 @@
         mChooserActions = extractCustomActions
                 ? getChooserActions(clientIntent)
                 : ImmutableList.of();
+        mReselectionAction = extractReslectionAction
+                ? getReselectionActionExtra(clientIntent)
+                : null;
     }
 
     public Intent getTargetIntent() {
@@ -182,6 +188,11 @@
         return mChooserActions;
     }
 
+    @Nullable
+    public PendingIntent getReselectionAction() {
+        return mReselectionAction;
+    }
+
     /**
      * Whether the {@link ChooserActivity.EXTRA_PRIVATE_RETAIN_IN_ON_STOP} behavior was requested.
      */
@@ -321,6 +332,21 @@
             .collect(toImmutableList());
     }
 
+    @Nullable
+    private static PendingIntent getReselectionActionExtra(Intent intent) {
+        try {
+            return intent.getParcelableExtra(
+                    Intent.EXTRA_CHOOSER_PAYLOAD_RESELECTION_ACTION,
+                    PendingIntent.class);
+        } catch (Throwable t) {
+            Log.w(
+                    TAG,
+                    "Unable to retrieve Intent.EXTRA_CHOOSER_PAYLOAD_RESELECTION_ACTION argument",
+                    t);
+            return null;
+        }
+    }
+
     private static <T> Collector<T, ?, ImmutableList<T>> toImmutableList() {
         return Collectors.collectingAndThen(Collectors.toList(), ImmutableList::copyOf);
     }