Fixing accessibility support in Recent Apps

- also switching "Recent applications" to "Recent apps" as per our new language guidelines

Change-Id: Ib625429ad22ce75ede782d59c0f45894d00c8502
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
index c6966f9..58355bd 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -44,20 +44,18 @@
                 android:layout_height="@dimen/status_bar_recents_thumbnail_height"
                 android:visibility="invisible"
             />
-        </FrameLayout>
 
-        <ImageView android:id="@+id/app_icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignLeft="@id/app_thumbnail"
-            android:layout_alignTop="@id/app_thumbnail"
-            android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
-            android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
-            android:maxWidth="@dimen/status_bar_recents_app_icon_max_width"
-            android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
-            android:adjustViewBounds="true"
-            android:visibility="invisible"
-        />
+            <ImageView android:id="@+id/app_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
+                android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
+                android:maxWidth="@dimen/status_bar_recents_app_icon_max_width"
+                android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
+                android:adjustViewBounds="true"
+                android:visibility="invisible"
+            />
+        </FrameLayout>
 
         <TextView android:id="@+id/app_label"
             android:layout_width="@dimen/status_bar_recents_app_label_width"
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
index 2fe22ff..3ee9e77 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -75,13 +75,4 @@
         android:layout_height="match_parent"
         android:visibility="invisible" />
 
-    <View android:id="@+id/recents_dismiss_button"
-        android:layout_width="80px"
-        android:layout_height="@*android:dimen/status_bar_height"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentLeft="true"
-        android:background="@drawable/ic_sysbar_back_ime"
-        android:visibility="gone"
-    />
-
 </com.android.systemui.recent.RecentsPanelView>
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
index 586712f..8c82eb1 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
@@ -42,20 +42,16 @@
                 android:layout_height="@dimen/status_bar_recents_thumbnail_height"
                 android:visibility="invisible"
             />
+            <ImageView android:id="@+id/app_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
+                android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
+                android:maxWidth="@dimen/status_bar_recents_app_icon_max_width"
+                android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
+                android:adjustViewBounds="true"
+            />
         </FrameLayout>
-
-        <ImageView android:id="@+id/app_icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignLeft="@id/app_thumbnail"
-            android:layout_alignTop="@id/app_thumbnail"
-            android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
-            android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
-            android:maxWidth="@dimen/status_bar_recents_app_icon_max_width"
-            android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
-            android:adjustViewBounds="true"
-        />
-
         <TextView android:id="@+id/app_label"
             android:layout_width="@dimen/status_bar_recents_app_label_width"
             android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
index 4d49077..d040544 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
@@ -73,13 +73,4 @@
         android:layout_height="match_parent"
         android:visibility="invisible" />
 
-    <View android:id="@+id/recents_dismiss_button"
-        android:layout_width="80px"
-        android:layout_height="@*android:dimen/status_bar_height"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentLeft="true"
-        android:background="@drawable/ic_sysbar_back_ime"
-        android:visibility="gone"
-    />
-
 </com.android.systemui.recent.RecentsPanelView>
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
index 125c87e..b96c357 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
@@ -73,7 +73,7 @@
                     android:layout_width="80dip"
                     android:layout_height="match_parent"
                     android:src="@drawable/ic_sysbar_recent"
-                    android:contentDescription="@string/accessibility_menu"
+                    android:contentDescription="@string/accessibility_recent"
                     systemui:glowBackground="@drawable/ic_sysbar_highlight"
                     />
                 <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
index cd8ccd5..e6336718 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -37,20 +37,17 @@
             android:layout_height="@dimen/status_bar_recents_thumbnail_height"
             android:visibility="invisible"
         />
+        <ImageView android:id="@+id/app_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
+            android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
+            android:maxWidth="@dimen/status_bar_recents_app_icon_max_width"
+            android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
+            android:adjustViewBounds="true"
+        />
     </FrameLayout>
 
-    <ImageView android:id="@+id/app_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignLeft="@id/app_thumbnail"
-        android:layout_alignTop="@id/app_thumbnail"
-        android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
-        android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
-        android:maxWidth="@dimen/status_bar_recents_app_icon_max_width"
-        android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
-        android:adjustViewBounds="true"
-    />
-
     <TextView android:id="@+id/app_label"
         android:layout_width="@dimen/status_bar_recents_app_label_width"
         android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
index 4ef602e..5dd101e 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
@@ -79,6 +79,7 @@
         android:layout_alignParentBottom="true"
         android:layout_alignParentLeft="true"
         android:background="@drawable/ic_sysbar_back_ime"
+        android:contentDescription="@string/status_bar_accessibility_dismiss_recents"
     />
 
 </com.android.systemui.recent.RecentsPanelView>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b9e6d78..26200ec 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -42,9 +42,19 @@
     <string name="status_bar_recent_inspect_item_title">App info</string>
 
     <!-- Message shown in the middle of the screen after clicking on the recent apps button
-         when there are no recent apps to show [CHAR LIMIT=45]-->
+         when there are no recent apps to show. Also used for accessibility. [CHAR LIMIT=45]-->
     <string name="status_bar_no_recent_apps">No recent apps</string>
 
+    <!-- Content description for the button to dismiss Recent Apps (only present on large
+         devices) -->
+    <string name="status_bar_accessibility_dismiss_recents">Dismiss recent apps</string>
+
+    <!-- Message that is read when you enter recent apps in TalkBack -->
+    <plurals name="status_bar_accessibility_recent_apps">
+        <item quantity="one">1 recent app</item>
+        <item quantity="other">%d recent apps</item>
+    </plurals>
+
     <!-- The label in the bar at the top of the status bar when there are no notifications
          showing.  [CHAR LIMIT=40]-->
     <string name="status_bar_no_notifications_title">No notifications</string>
@@ -109,14 +119,6 @@
     <!-- Separator for PLMN and SPN in network name. -->
     <string name="status_bar_network_name_separator" translatable="false">"\n"</string>
 
-    <!-- Recent Tasks dialog: title [CHAR LIMIT=30] -->
-    <string name="recent_tasks_title">Recent</string>
-    <!-- Recent Tasks dialog: message when there are no recent applications [CHAR LIMIT=NONE]-->
-    <string name="recent_tasks_empty">No recent applications.</string>
-
-    <!-- Recent apps label. Shown as title on recent apps panel -->
-    <string name="recent_tasks_app_label">Apps</string>
-
     <!-- Network connection string for Bluetooth Reverse Tethering -->
     <string name="bluetooth_tethered">Bluetooth tethered</string>
     <!-- Title of a button to open the settings for input methods [CHAR LIMIT=30] -->
@@ -187,7 +189,7 @@
     <!-- Content description of the menu button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_menu">Menu</string>
     <!-- Content description of the recents button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_recent">Recent applications</string>
+    <string name="accessibility_recent">Recent apps</string>
 
     <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_ime_switch_button">Switch input method button.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 1c9d80d..58af255 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -26,6 +26,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.View.OnTouchListener;
 import android.widget.HorizontalScrollView;
 import android.widget.LinearLayout;
 
@@ -65,6 +66,13 @@
                 mPerformanceHelper.addViewCallback(view);
             }
 
+            OnTouchListener noOpListener = new OnTouchListener() {
+                @Override
+                public boolean onTouch(View v, MotionEvent event) {
+                    return true;
+                }
+            };
+
             view.setOnClickListener(new OnClickListener() {
                 public void onClick(View v) {
                     mCallback.dismiss();
@@ -78,22 +86,25 @@
                     mCallback.handleOnClick(view);
                 }
             };
+
+            final View thumbnailView = view.findViewById(R.id.app_thumbnail);
             OnLongClickListener longClickListener = new OnLongClickListener() {
                 public boolean onLongClick(View v) {
                     final View anchorView = view.findViewById(R.id.app_description);
-                    final View thumbnailView = view.findViewById(R.id.app_thumbnail);
                     mCallback.handleLongPress(view, anchorView, thumbnailView);
                     return true;
                 }
             };
-            final View thumbnail = view.findViewById(R.id.app_thumbnail);
-            thumbnail.setClickable(true);
-            thumbnail.setOnClickListener(launchAppListener);
-            thumbnail.setOnLongClickListener(longClickListener);
+            thumbnailView.setClickable(true);
+            thumbnailView.setOnClickListener(launchAppListener);
+            thumbnailView.setOnLongClickListener(longClickListener);
+
+            // We don't want to dismiss recents if a user clicks on the app title
+            // (we also don't want to launch the app either, though, because the
+            // app title is a small target and doesn't have great click feedback)
             final View appTitle = view.findViewById(R.id.app_label);
-            appTitle.setClickable(true);
-            appTitle.setOnClickListener(launchAppListener);
-            appTitle.setOnLongClickListener(longClickListener);
+            appTitle.setContentDescription(" ");
+            appTitle.setOnTouchListener(noOpListener);
             mLinearLayout.addView(view);
         }
         // Scroll to end after layout.
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 54bc4e3..ca9e273 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -32,12 +32,9 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
 import android.graphics.Shader.TileMode;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -56,7 +53,6 @@
 import android.view.animation.AnimationUtils;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.PopupMenu;
@@ -202,6 +198,7 @@
             holder.descriptionView.setText(activityDescription.recentTaskInfo.description);
             holder.thumbnailView.setTag(activityDescription);
             holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView));
+            holder.thumbnailView.setContentDescription(activityDescription.getLabel());
             holder.activityDescription = activityDescription;
 
             return convertView;
@@ -228,6 +225,23 @@
     }
 
     public void show(boolean show, boolean animate) {
+        if (show) {
+            // Need to update list of recent apps before we set visibility so this view's
+            // content description is updated before it gets focus for TalkBack mode
+            refreshApplicationList();
+
+            // if there are no apps, either bring up a "No recent apps" message, or just
+            // quit early
+            boolean noApps = (mActivityDescriptions.size() == 0);
+            if (mRecentsNoApps != null) { // doesn't exist on large devices
+                mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE);
+            } else {
+                if (noApps) {
+                    if (DEBUG) Log.v(TAG, "Nothing to show");
+                    return;
+                }
+            }
+        }
         if (animate) {
             if (mShowing != show) {
                 mShowing = show;
@@ -378,11 +392,13 @@
         mRecentsNoApps = findViewById(R.id.recents_no_apps);
         mChoreo = new Choreographer(this, mRecentsScrim, mRecentsGlowView, mRecentsNoApps, this);
         mRecentsDismissButton = findViewById(R.id.recents_dismiss_button);
-        mRecentsDismissButton.setOnClickListener(new OnClickListener() {
-            public void onClick(View v) {
-                hide(true);
-            }
-        });
+        if (mRecentsDismissButton != null) {
+            mRecentsDismissButton.setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    hide(true);
+                }
+            });
+        }
 
         // In order to save space, we make the background texture repeat in the Y direction
         if (mRecentsScrim != null && mRecentsScrim.getBackground() instanceof BitmapDrawable) {
@@ -400,9 +416,6 @@
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
         if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + changedView + ", " + visibility + ")");
-        if (visibility == View.VISIBLE && changedView == this) {
-            refreshApplicationList();
-        }
 
         if (mRecentsContainer instanceof RecentsHorizontalScrollView) {
             ((RecentsHorizontalScrollView) mRecentsContainer).onRecentsVisibilityChanged();
@@ -553,6 +566,7 @@
                             }
                             h.iconView.setVisibility(View.VISIBLE);
                             h.labelView.setText(ad.getLabel());
+                            h.thumbnailView.setContentDescription(ad.getLabel());
                             if (anim) {
                                 h.labelView.setAnimation(AnimationUtils.loadAnimation(
                                         mContext, R.anim.recent_appear));
@@ -582,10 +596,18 @@
             mThumbnailLoader.cancel(false);
             mThumbnailLoader = null;
         }
-        if (mRecentsNoApps != null) { // doesn't exist on large devices
-            mRecentsNoApps.setVisibility(View.INVISIBLE);
-        }
+
         mActivityDescriptions = getRecentTasks();
+        int numRecentApps = mActivityDescriptions.size();
+        String recentAppsAccessibilityDescription;
+        if (numRecentApps == 0) {
+            recentAppsAccessibilityDescription =
+                getResources().getString(R.string.status_bar_no_recent_apps);
+        } else {
+            recentAppsAccessibilityDescription = getResources().getQuantityString(
+                R.plurals.status_bar_accessibility_recent_apps, numRecentApps, numRecentApps);
+        }
+        setContentDescription(recentAppsAccessibilityDescription);
         for (ActivityDescription ad : mActivityDescriptions) {
             ad.setThumbnail(mDefaultThumbnailBackground);
         }
@@ -649,14 +671,6 @@
                 };
                 mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
             }
-        } else {
-            // Immediately hide this panel
-            if (DEBUG) Log.v(TAG, "Nothing to show");
-            if (mRecentsNoApps != null) { // doesn't exist on large devices
-                mRecentsNoApps.setVisibility(View.VISIBLE);
-            } else {
-                hide(false);
-            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index 213803c..7853402 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -74,6 +74,13 @@
             }
 
             if (old == null) {
+                OnTouchListener noOpListener = new OnTouchListener() {
+                    @Override
+                    public boolean onTouch(View v, MotionEvent event) {
+                        return true;
+                    }
+                };
+
                 view.setOnClickListener(new OnClickListener() {
                     public void onClick(View v) {
                         mCallback.dismiss();
@@ -87,26 +94,27 @@
                         mCallback.handleOnClick(view);
                     }
                 };
+
+                final View thumbnailView = view.findViewById(R.id.app_thumbnail);
                 OnLongClickListener longClickListener = new OnLongClickListener() {
                     public boolean onLongClick(View v) {
                         final View anchorView = view.findViewById(R.id.app_description);
-                        final View thumbnailView = view.findViewById(R.id.app_thumbnail);
                         mCallback.handleLongPress(view, anchorView, thumbnailView);
                         return true;
                     }
                 };
-                final View thumbnail = view.findViewById(R.id.app_thumbnail);
-                thumbnail.setClickable(true);
-                thumbnail.setOnClickListener(launchAppListener);
-                thumbnail.setOnLongClickListener(longClickListener);
+                thumbnailView.setClickable(true);
+                thumbnailView.setOnClickListener(launchAppListener);
+                thumbnailView.setOnLongClickListener(longClickListener);
+
+                // We don't want to dismiss recents if a user clicks on the app title
+                // (we also don't want to launch the app either, though, because the
+                // app title is a small target and doesn't have great click feedback)
                 final View appTitle = view.findViewById(R.id.app_label);
-                appTitle.setClickable(true);
-                appTitle.setOnClickListener(launchAppListener);
-                appTitle.setOnLongClickListener(longClickListener);
+                appTitle.setContentDescription(" ");
+                appTitle.setOnTouchListener(noOpListener);
                 final View calloutLine = view.findViewById(R.id.recents_callout_line);
-                calloutLine.setClickable(true);
-                calloutLine.setOnClickListener(launchAppListener);
-                calloutLine.setOnLongClickListener(longClickListener);
+                calloutLine.setOnTouchListener(noOpListener);
                 mLinearLayout.addView(view);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e5d4d22..79abfed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -192,7 +192,7 @@
     private View mTickerView;
     private boolean mTicking;
 
-    // Recent applications
+    // Recent apps
     private RecentsPanelView mRecentsPanel;
 
     // Tracking finger for opening/closing.
@@ -417,9 +417,6 @@
         WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
         mRecentsPanel.setBar(this);
         if (visible) {
-            // need to set visibility to View.GONE earlier since that
-            // triggers refreshing application list
-            mRecentsPanel.setVisibility(View.VISIBLE);
             mRecentsPanel.show(true, false);
         }
 
@@ -1126,7 +1123,6 @@
                 case MSG_OPEN_RECENTS_PANEL:
                     if (DEBUG) Slog.d(TAG, "opening recents panel");
                     if (mRecentsPanel != null) {
-                        mRecentsPanel.setVisibility(View.VISIBLE);
                         mRecentsPanel.show(true, true);
                     }
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 435aa8c..54b45a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -714,7 +714,6 @@
                 case MSG_OPEN_RECENTS_PANEL:
                     if (DEBUG) Slog.d(TAG, "opening recents panel");
                     if (mRecentsPanel != null) {
-                        mRecentsPanel.setVisibility(View.VISIBLE);
                         mRecentsPanel.show(true, true);
                     }
                     break;