Merge "Touch-exploration improvements to volume dialog." into lmp-dev
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 48450dd..1a44c8c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -908,4 +908,7 @@
 
     <!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] -->
     <string name="muted_by">Muted by <xliff:g id="third_party">%1$s</xliff:g></string>
+
+    <!-- Accessibility string for current zen mode and selected exit condition. A template that simply concatenates existing mode string and the current condition description. [CHAR LIMIT=20] -->
+    <string name="zen_mode_and_condition"><xliff:g id="zen_mode" example="Priority interruptions only">%1$s</xliff:g>. <xliff:g id="exit_condition" example="For one hour">%2$s</xliff:g></string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Interaction.java b/packages/SystemUI/src/com/android/systemui/volume/Interaction.java
new file mode 100644
index 0000000..46eab36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/Interaction.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnGenericMotionListener;
+import android.view.View.OnTouchListener;
+
+public class Interaction {
+
+    public static void register(View v, final Callback callback) {
+        v.setOnTouchListener(new OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                callback.onInteraction();
+                return false;
+            }
+        });
+        v.setOnGenericMotionListener(new OnGenericMotionListener() {
+            @Override
+            public boolean onGenericMotion(View v, MotionEvent event) {
+                callback.onInteraction();
+                return false;
+            }
+        });
+    }
+
+    public interface Callback {
+        void onInteraction();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
index 66e1e15..f7f5047 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -80,6 +80,12 @@
         addView(b);
         b.setTag(value);
         b.setOnClickListener(mClick);
+        Interaction.register(b, new Interaction.Callback() {
+            @Override
+            public void onInteraction() {
+                fireInteraction();
+            }
+        });
     }
 
     public void updateLocale() {
@@ -96,6 +102,12 @@
         }
     }
 
+    private void fireInteraction() {
+        if (mCallback != null) {
+            mCallback.onInteraction();
+        }
+    }
+
     private final View.OnClickListener mClick = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -103,7 +115,7 @@
         }
     };
 
-    public interface Callback {
+    public interface Callback extends Interaction.Callback {
         void onSelected(Object value);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index fa43f32..40bdea2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -42,6 +42,7 @@
 import android.media.session.MediaController;
 import android.media.session.MediaController.PlaybackInfo;
 import android.net.Uri;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Vibrator;
@@ -400,11 +401,10 @@
                 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
         mView = window.findViewById(R.id.content);
-        mView.setOnTouchListener(new View.OnTouchListener() {
+        Interaction.register(mView, new Interaction.Callback() {
             @Override
-            public boolean onTouch(View v, MotionEvent event) {
+            public void onInteraction() {
                 resetTimeout();
-                return false;
             }
         });
 
@@ -1382,9 +1382,10 @@
     }
 
     private void resetTimeout() {
+        final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
         if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
-                + " delay=" + mTimeoutDelay);
-        if (sSafetyWarning == null || !mAccessibilityManager.isTouchExplorationEnabled()) {
+                + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
+        if (sSafetyWarning == null || !touchExploration) {
             removeMessages(MSG_TIMEOUT);
             sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
             removeMessages(MSG_USER_ACTIVITY);
@@ -1393,6 +1394,7 @@
     }
 
     private void forceTimeout(long delay) {
+        if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
         removeMessages(MSG_TIMEOUT);
         sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index c1681c7..ea431ae 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -135,20 +135,21 @@
             @Override
             public void onClick(View v) {
                 setExpanded(true);
-                fireInteraction();
             }
         });
+        Interaction.register(mZenSubheadCollapsed, mInteractionCallback);
 
         mZenSubheadExpanded = (TextView) findViewById(R.id.zen_subhead_expanded);
+        Interaction.register(mZenSubheadExpanded, mInteractionCallback);
 
         mMoreSettings = findViewById(R.id.zen_more_settings);
         mMoreSettings.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 fireMoreSettings();
-                fireInteraction();
             }
         });
+        Interaction.register(mMoreSettings, mInteractionCallback);
 
         mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
     }
@@ -444,18 +445,22 @@
                         childTag.rb.setChecked(false);
                     }
                     select(tag.condition);
-                    fireInteraction();
+                    announceConditionSelection(tag);
                 }
             }
         });
-        final TextView title = (TextView) row.findViewById(android.R.id.title);
-        if (condition == null) {
-            title.setText(mContext.getString(com.android.internal.R.string.zen_mode_forever));
-        } else {
-            title.setText(condition.summary);
+
+        if (tag.title == null) {
+            tag.title = (TextView) row.findViewById(android.R.id.title);
         }
-        title.setEnabled(enabled);
-        title.setAlpha(enabled ? 1 : .4f);
+        if (condition == null) {
+            tag.title.setText(mContext.getString(com.android.internal.R.string.zen_mode_forever));
+        } else {
+            tag.title.setText(condition.summary);
+        }
+        tag.title.setEnabled(enabled);
+        tag.title.setAlpha(enabled ? 1 : .4f);
+
         final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
         button1.setOnClickListener(new OnClickListener() {
             @Override
@@ -471,11 +476,10 @@
                 onClickTimeButton(row, tag, true /*up*/);
             }
         });
-        title.setOnClickListener(new OnClickListener() {
+        tag.title.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
                 tag.rb.setChecked(true);
-                fireInteraction();
             }
         });
 
@@ -497,6 +501,30 @@
             button1.setVisibility(View.GONE);
             button2.setVisibility(View.GONE);
         }
+        // wire up interaction callbacks for newly-added condition rows
+        if (convertView == null) {
+            Interaction.register(tag.rb, mInteractionCallback);
+            Interaction.register(tag.title, mInteractionCallback);
+            Interaction.register(button1, mInteractionCallback);
+            Interaction.register(button2, mInteractionCallback);
+        }
+    }
+
+    private void announceConditionSelection(ConditionTag tag) {
+        final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
+        String modeText;
+        switch(zen) {
+            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+                modeText = mContext.getString(R.string.zen_important_interruptions);
+                break;
+            case Global.ZEN_MODE_NO_INTERRUPTIONS:
+                modeText = mContext.getString(R.string.zen_no_interruptions);
+                break;
+             default:
+                return;
+        }
+        announceForAccessibility(mContext.getString(R.string.zen_mode_and_condition, modeText,
+                tag.title.getText()));
     }
 
     private void onClickTimeButton(View row, ConditionTag tag, boolean up) {
@@ -530,7 +558,7 @@
         bind(mTimeCondition, row);
         tag.rb.setChecked(true);
         select(mTimeCondition);
-        fireInteraction();
+        announceConditionSelection(tag);
     }
 
     private void select(Condition condition) {
@@ -611,6 +639,7 @@
     // used as the view tag on condition rows
     private static class ConditionTag {
         RadioButton rb;
+        TextView title;
         Condition condition;
     }
 
@@ -691,5 +720,17 @@
                 mController.setZen((Integer) value);
             }
         }
+
+        @Override
+        public void onInteraction() {
+            fireInteraction();
+        }
+    };
+
+    private final Interaction.Callback mInteractionCallback = new Interaction.Callback() {
+        @Override
+        public void onInteraction() {
+            fireInteraction();
+        }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenToast.java b/packages/SystemUI/src/com/android/systemui/volume/ZenToast.java
index 96e2a8e..d887712 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenToast.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenToast.java
@@ -31,6 +31,7 @@
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
 import android.view.WindowManager;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -110,6 +111,17 @@
         message.setText(text);
         final ImageView icon = (ImageView) mZenToast.findViewById(android.R.id.icon);
         icon.setImageResource(iconRes);
+        mZenToast.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+                // noop
+            }
+
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                mZenToast.announceForAccessibility(message.getText());
+            }
+        });
         mWindowManager.addView(mZenToast, params);
         final int animDuration = res.getInteger(R.integer.zen_toast_animation_duration);
         final int visibleDuration = res.getInteger(R.integer.zen_toast_visible_duration);