Merge "Handle duplicate backlinks in app clips" into main
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e6cc6cf..fdba2e6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -270,6 +270,7 @@
     <!-- Add to note button used in App Clips flow to return the saved screenshot image to notes app. [CHAR LIMIT=NONE] -->
     <string name="app_clips_save_add_to_note">Add to note</string>
     <string name="backlinks_include_link">Include link</string>
+    <string name="backlinks_duplicate_label_format"><xliff:g id="appName" example="Google Chrome">%1$s</xliff:g> <xliff:g id="frequencyCount" example="(1)">(%2$d)</xliff:g></string>
 
     <!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
     <string name="screenrecord_title">Screen Recorder</string>
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index 9db1f24..ad5e772 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -71,7 +71,9 @@
 import com.android.systemui.screenshot.scroll.CropView;
 import com.android.systemui.settings.UserTracker;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -344,10 +346,63 @@
 
         // Set up the dropdown when multiple backlinks are available.
         if (backlinksData.size() > 1) {
-            setUpListPopupWindow(backlinksData, mBacklinksDataTextView);
+            setUpListPopupWindow(updateBacklinkLabelsWithDuplicateNames(backlinksData),
+                    mBacklinksDataTextView);
         }
     }
 
+    /**
+     * If there are more than 1 backlinks that have the same app name, then this method appends
+     * a numerical suffix to such backlinks to help users distinguish.
+     */
+    private List<InternalBacklinksData> updateBacklinkLabelsWithDuplicateNames(
+            List<InternalBacklinksData> backlinksData) {
+        // Check if there are multiple backlinks with same name.
+        Map<String, Integer> duplicateNamedBacklinksCountMap = new HashMap<>();
+        for (InternalBacklinksData data : backlinksData) {
+            if (duplicateNamedBacklinksCountMap.containsKey(data.getDisplayLabel())) {
+                int duplicateCount = duplicateNamedBacklinksCountMap.get(data.getDisplayLabel());
+                if (duplicateCount == 0) {
+                    // If this is the first time the loop is coming across a duplicate name, set the
+                    // count to 2. This way the count starts from 1 for all duplicate named
+                    // backlinks.
+                    duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), 2);
+                } else {
+                    // For all duplicate named backlinks, increase the duplicate count by 1.
+                    duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), duplicateCount + 1);
+                }
+            } else {
+                // This is the first time the loop is coming across a backlink with this name. Set
+                // its count to 0. The loop will increase its count by 1 when a duplicate is found.
+                duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), 0);
+            }
+        }
+
+        // Go through the backlinks in reverse order as it is easier to assign the numerical suffix
+        // in descending order of frequency using the duplicate map that was built earlier. For
+        // example, if "App A" is present 3 times, then we assign display label "App A (3)" first
+        // and then "App A (2)", lastly "App A (1)".
+        for (InternalBacklinksData data : backlinksData.reversed()) {
+            String originalBacklinkLabel = data.getDisplayLabel();
+            int duplicateCount = duplicateNamedBacklinksCountMap.get(originalBacklinkLabel);
+
+            // The display label should only be updated if there are multiple backlinks with the
+            // same name.
+            if (duplicateCount > 0) {
+                // Update the display label to: "App name (count)"
+                data.setDisplayLabel(
+                        getString(R.string.backlinks_duplicate_label_format, originalBacklinkLabel,
+                                duplicateCount));
+
+                // Decrease the duplicate count and update the map.
+                duplicateCount--;
+                duplicateNamedBacklinksCountMap.put(originalBacklinkLabel, duplicateCount);
+            }
+        }
+
+        return backlinksData;
+    }
+
     private void setUpListPopupWindow(List<InternalBacklinksData> backlinksData, View anchor) {
         ListPopupWindow listPopupWindow = new ListPopupWindow(this);
         listPopupWindow.setAnchorView(anchor);
@@ -365,7 +420,7 @@
             public View getView(int position, @Nullable View convertView, ViewGroup parent) {
                 TextView itemView = (TextView) super.getView(position, convertView, parent);
                 InternalBacklinksData data = backlinksData.get(position);
-                itemView.setText(data.getClipData().getDescription().getLabel());
+                itemView.setText(data.getDisplayLabel());
 
                 Drawable icon = data.getAppIcon();
                 icon.setBounds(createBacklinksTextViewDrawableBounds());
@@ -387,7 +442,7 @@
      * expected to be already set when this method is called.
      */
     private void updateBacklinksTextView(InternalBacklinksData backlinksData) {
-        mBacklinksDataTextView.setText(backlinksData.getClipData().getDescription().getLabel());
+        mBacklinksDataTextView.setText(backlinksData.getDisplayLabel());
         Drawable appIcon = backlinksData.getAppIcon();
         Rect compoundDrawableBounds = createBacklinksTextViewDrawableBounds();
         appIcon.setBounds(compoundDrawableBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
index 0e312f9..30c33c5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
@@ -20,4 +20,6 @@
 import android.graphics.drawable.Drawable
 
 /** A class to hold the [ClipData] for backlinks and the corresponding app's [Drawable] icon. */
-internal data class InternalBacklinksData(val clipData: ClipData, val appIcon: Drawable)
+internal data class InternalBacklinksData(val clipData: ClipData, val appIcon: Drawable) {
+    var displayLabel: String = clipData.description.label.toString()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
index a8d5008..eb1a04d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
@@ -308,6 +308,40 @@
         assertThat(backlinksData.getCompoundDrawablesRelative()[2]).isNotNull();
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_APP_CLIPS_BACKLINKS)
+    public void appClipsLaunched_backlinks_multipleBacklinksAvailable_duplicateName()
+            throws RemoteException {
+        // Set up mocking for multiple backlinks.
+        ResolveInfo resolveInfo1 = createBacklinksTaskResolveInfo();
+
+        ResolveInfo resolveInfo2 = createBacklinksTaskResolveInfo();
+        RunningTaskInfo runningTaskInfo2 = createTaskInfoForBacklinksTask();
+        int taskId2 = BACKLINKS_TASK_ID + 2;
+        runningTaskInfo2.taskId = taskId2;
+
+        when(mAtmService.getTasks(eq(Integer.MAX_VALUE), eq(false), eq(false),
+                mDisplayIdCaptor.capture()))
+                .thenReturn(List.of(TASK_THAT_SUPPORTS_BACKLINKS, runningTaskInfo2));
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(resolveInfo1,
+                resolveInfo1, resolveInfo1, resolveInfo2, resolveInfo2, resolveInfo2);
+        when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
+
+        // Using same AssistContent data for both tasks.
+        mockForAssistContent(ASSIST_CONTENT_FOR_BACKLINKS_TASK, BACKLINKS_TASK_ID);
+        mockForAssistContent(ASSIST_CONTENT_FOR_BACKLINKS_TASK, taskId2);
+
+        // Mocking complete, trigger backlinks.
+        launchActivity();
+        waitForIdleSync();
+
+        // Verify default backlink shown to user has the numerical suffix.
+        TextView backlinksData = mActivity.findViewById(R.id.backlinks_data);
+        assertThat(backlinksData.getText().toString()).isEqualTo(
+                mContext.getString(R.string.backlinks_duplicate_label_format,
+                        BACKLINKS_TASK_APP_NAME, 1));
+    }
+
     private void setUpMocksForBacklinks() throws RemoteException {
         when(mAtmService.getTasks(eq(Integer.MAX_VALUE), eq(false), eq(false),
                 mDisplayIdCaptor.capture()))