Merge "Improve Accessibility behaviour in recents" into mnc-dev
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f3a2912..00b8df2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -400,6 +400,8 @@
     <string name="accessibility_recents_all_items_dismissed">All recent applications dismissed.</string>
     <!-- Content description to tell the user an application has been launched from recents -->
     <string name="accessibility_recents_item_launched">Starting <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
+    <!-- Content description of individual recents task. -->
+    <string name="accessibility_recents_task_header"><xliff:g id="app" example="Chrome">%1$s</xliff:g> <xliff:g id="activity_label" example="www.google.com">%2$s</xliff:g></string>
     <!-- Content description to tell the user a notification has been removed from the notification shade -->
     <string name="accessibility_notification_dismissed">Notification dismissed.</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index d60df9c..e3fb16a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -438,6 +438,33 @@
         return info.loadLabel(mPm).toString();
     }
 
+    /** Returns the application label */
+    public String getApplicationLabel(Intent baseIntent, int userId) {
+        if (mPm == null) return null;
+
+        // If we are mocking, then return a mock label
+        if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
+            return "Recent Task";
+        }
+
+        ResolveInfo ri = mPm.resolveActivityAsUser(baseIntent, 0, userId);
+        CharSequence label = (ri != null) ? ri.loadLabel(mPm) : null;
+        return (label != null) ? label.toString() : null;
+    }
+
+    /** Returns the content description for a given task */
+    public String getContentDescription(Intent baseIntent, int userId, String activityLabel,
+            Resources res) {
+        String applicationLabel = getApplicationLabel(baseIntent, userId);
+        if (applicationLabel == null) {
+            return getBadgedLabel(activityLabel, userId);
+        }
+        String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
+        return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
+                : res.getString(R.string.accessibility_recents_task_header,
+                        badgedApplicationLabel, activityLabel);
+    }
+
     /**
      * Returns the activity icon for the ActivityInfo for a user, badging if
      * necessary.
@@ -464,6 +491,16 @@
         return icon;
     }
 
+    /**
+     * Returns the given label for a user, badging if necessary.
+     */
+    public String getBadgedLabel(String label, int userId) {
+        if (userId != UserHandle.myUserId()) {
+            label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
+        }
+        return label;
+    }
+
     /** Returns the package name of the home activity. */
     public String getHomeActivityPackageName() {
         if (mPm == null) return null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 5d98dda..40cd211 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -130,6 +130,8 @@
             // Load the label, icon, and color
             String activityLabel = loader.getAndUpdateActivityLabel(taskKey, t.taskDescription,
                     mSystemServicesProxy, infoHandle);
+            String contentDescription = loader.getAndUpdateContentDescription(taskKey,
+                    activityLabel, mSystemServicesProxy, res);
             Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
                     mSystemServicesProxy, res, infoHandle, false);
             int activityColor = loader.getActivityPrimaryColor(t.taskDescription, mConfig);
@@ -148,9 +150,9 @@
 
             // Add the task to the stack
             Task task = new Task(taskKey, (t.id != RecentsTaskLoader.INVALID_TASK_ID),
-                    t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, activityIcon,
-                    activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled, icon,
-                    iconFilename);
+                    t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, contentDescription,
+                    activityIcon, activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled,
+                    icon, iconFilename);
             task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false);
             if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 3192fe6..b2aa2b6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -259,6 +259,7 @@
     DrawableLruCache mApplicationIconCache;
     BitmapLruCache mThumbnailCache;
     StringLruCache mActivityLabelCache;
+    StringLruCache mContentDescriptionCache;
     TaskResourceLoadQueue mLoadQueue;
     TaskResourceLoader mLoader;
 
@@ -298,6 +299,7 @@
         mApplicationIconCache = new DrawableLruCache(iconCacheSize);
         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
         mActivityLabelCache = new StringLruCache(100);
+        mContentDescriptionCache = new StringLruCache(100);
         mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
                 mDefaultThumbnail, mDefaultApplicationIcon);
     }
@@ -348,6 +350,24 @@
         return label;
     }
 
+    /** Returns the content description using as many cached values as we can. */
+    public String getAndUpdateContentDescription(Task.TaskKey taskKey, String activityLabel,
+            SystemServicesProxy ssp, Resources res) {
+        // Return the cached content description if it exists
+        String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
+        if (label != null) {
+            return label;
+        }
+        label = ssp.getContentDescription(taskKey.baseIntent, taskKey.userId, activityLabel, res);
+        if (label != null) {
+            mContentDescriptionCache.put(taskKey, label);
+        } else {
+            Log.w(TAG, "Missing content description for " + taskKey.baseIntent.getComponent()
+                    + " u=" + taskKey.userId);
+        }
+        return label;
+    }
+
     /** Returns the activity icon using as many cached values as we can. */
     public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey,
             ActivityManager.TaskDescription td, SystemServicesProxy ssp,
@@ -541,6 +561,7 @@
                 mApplicationIconCache.evictAll();
                 // The cache is small, only clear the label cache when we are critical
                 mActivityLabelCache.evictAll();
+                mContentDescriptionCache.evictAll();
                 break;
             default:
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 0cd55d7..c14adf4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -124,6 +124,7 @@
     public boolean isLaunchTarget;
     public Drawable applicationIcon;
     public Drawable activityIcon;
+    public String contentDescription;
     public String activityLabel;
     public int colorPrimary;
     public boolean useLightOnPrimaryColor;
@@ -140,8 +141,8 @@
     }
 
     public Task(TaskKey key, boolean isActive, int taskAffiliation, int taskAffiliationColor,
-                String activityTitle, Drawable activityIcon, int colorPrimary,
-                boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon,
+                String activityTitle, String contentDescription, Drawable activityIcon,
+                int colorPrimary, boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon,
                 String iconFilename) {
         boolean isInAffiliationGroup = (taskAffiliation != key.id);
         boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0);
@@ -149,6 +150,7 @@
         this.taskAffiliation = taskAffiliation;
         this.taskAffiliationColor = taskAffiliationColor;
         this.activityLabel = activityTitle;
+        this.contentDescription = contentDescription;
         this.activityIcon = activityIcon;
         this.colorPrimary = hasAffiliationGroupColor ? taskAffiliationColor : colorPrimary;
         this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
@@ -166,6 +168,7 @@
         this.taskAffiliation = o.taskAffiliation;
         this.taskAffiliationColor = o.taskAffiliationColor;
         this.activityLabel = o.activityLabel;
+        this.contentDescription = o.contentDescription;
         this.activityIcon = o.activityIcon;
         this.colorPrimary = o.colorPrimary;
         this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 60a91bf..f397bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -190,10 +190,12 @@
         } else if (t.applicationIcon != null) {
             mApplicationIcon.setImageDrawable(t.applicationIcon);
         }
-        mApplicationIcon.setContentDescription(t.activityLabel);
+        mApplicationIcon.setContentDescription(t.contentDescription);
         if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
             mActivityDescription.setText(t.activityLabel);
         }
+        mActivityDescription.setContentDescription(t.contentDescription);
+
         // Try and apply the system ui tint
         int existingBgColor = getBackgroundColor();
         if (existingBgColor != t.colorPrimary) {
@@ -207,7 +209,7 @@
         mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
                 mLightDismissDrawable : mDarkDismissDrawable);
         mDismissButton.setContentDescription(String.format(mDismissContentDescription,
-                t.activityLabel));
+                t.contentDescription));
         mMoveTaskButton.setVisibility((mConfig.multiStackEnabled) ? View.VISIBLE : View.INVISIBLE);
         if (mConfig.multiStackEnabled) {
             updateResizeTaskBarIcon(t);