Loading packages/SystemUI/res/values/strings.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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> Loading packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java +58 −3 Original line number Diff line number Diff line Loading @@ -71,7 +71,9 @@ import com.android.systemui.res.R; 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; Loading Loading @@ -344,10 +346,63 @@ public class AppClipsActivity extends ComponentActivity { // 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); Loading @@ -365,7 +420,7 @@ public class AppClipsActivity extends ComponentActivity { 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()); Loading @@ -387,7 +442,7 @@ public class AppClipsActivity extends ComponentActivity { * 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); Loading packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt +3 −1 Original line number Diff line number Diff line Loading @@ -20,4 +20,6 @@ import android.content.ClipData 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() } packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -308,6 +308,40 @@ public final class AppClipsActivityTest extends SysuiTestCase { 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())) Loading Loading
packages/SystemUI/res/values/strings.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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> Loading
packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java +58 −3 Original line number Diff line number Diff line Loading @@ -71,7 +71,9 @@ import com.android.systemui.res.R; 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; Loading Loading @@ -344,10 +346,63 @@ public class AppClipsActivity extends ComponentActivity { // 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); Loading @@ -365,7 +420,7 @@ public class AppClipsActivity extends ComponentActivity { 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()); Loading @@ -387,7 +442,7 @@ public class AppClipsActivity extends ComponentActivity { * 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); Loading
packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt +3 −1 Original line number Diff line number Diff line Loading @@ -20,4 +20,6 @@ import android.content.ClipData 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() }
packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -308,6 +308,40 @@ public final class AppClipsActivityTest extends SysuiTestCase { 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())) Loading