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()))