Zen: Improve zen mode panel condition selection.

 - Decouple condition requests from expansion, now
   pre-request when zen panel unhidden.
 - Animate zen mode panel expansion.
 - Improve default selection logic, ensure something
   is selected as soon as we are in the expanded state.
 - Tweak visual spacing.
 - Map null condition to Indef properly when we start
   out in zen.
 - Avoid unnecessary condition teardown when the conditions
   are updated but unchanged from current.
 - Cap number of optional conditions to display, default=3.

Bug: 18335618

Change-Id: I007b7c3b2e75e2b42805af240684aa8581e9951a
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 3a91d1a..80bdbf1 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -160,7 +160,7 @@
     }
 
     public static boolean isValidId(Uri id, String pkg) {
-        return id != null && id.getScheme().equals(SCHEME) && id.getAuthority().equals(pkg);
+        return id != null && SCHEME.equals(id.getScheme()) && pkg.equals(id.getAuthority());
     }
 
     public static final Parcelable.Creator<Condition> CREATOR
diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml
index a9ad1fc..0b91913 100644
--- a/packages/SystemUI/res/layout/zen_mode_condition.xml
+++ b/packages/SystemUI/res/layout/zen_mode_condition.xml
@@ -17,14 +17,15 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="@dimen/qs_detail_item_height"
+    android:layout_marginBottom="@dimen/zen_mode_condition_detail_item_spacing"
     android:layout_marginStart="@dimen/zen_mode_condition_detail_button_padding"
     android:layout_marginEnd="@dimen/zen_mode_condition_detail_button_padding" >
 
     <RadioButton
         android:id="@android:id/checkbox"
         android:layout_width="40dp"
-        android:layout_marginStart="2dp"
-        android:layout_marginEnd="1dp"
+        android:layout_marginStart="7dp"
+        android:layout_marginEnd="4dp"
         android:layout_height="match_parent"
         android:layout_alignParentStart="true"
         android:gravity="center" />
@@ -51,6 +52,7 @@
             android:id="@android:id/text2"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/zen_mode_condition_detail_item_interline_spacing"
             android:ellipsize="end"
             android:textAlignment="viewStart"
             android:maxLines="1"
@@ -64,7 +66,6 @@
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:layout_centerVertical="true"
-        android:layout_marginEnd="@dimen/zen_mode_condition_detail_button_padding"
         android:scaleType="center"
         android:layout_toStartOf="@android:id/button2"
         android:contentDescription="@string/accessibility_quick_settings_less_time"
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index f2dc402..922f90d 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -58,6 +58,8 @@
             android:background="@drawable/btn_borderless_rect"
             android:clickable="true"
             android:drawableEnd="@drawable/qs_subhead_caret"
+            android:maxLines="2"
+            android:ellipsize="end"
             android:textAppearance="@style/TextAppearance.QS.Subhead" />
 
         <TextView
@@ -67,6 +69,8 @@
             android:layout_gravity="center_vertical"
             android:gravity="center_vertical"
             android:paddingLeft="8dp"
+            android:maxLines="2"
+            android:ellipsize="end"
             android:textAppearance="@style/TextAppearance.QS.Subhead" />
 
         <ImageView
@@ -87,6 +91,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        android:paddingBottom="@dimen/qs_panel_padding" />
+        android:paddingBottom="@dimen/zen_mode_condition_detail_bottom_padding" />
 
 </com.android.systemui.volume.ZenModePanel>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b299f35..9a95b37 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -278,6 +278,9 @@
     <!-- Number of times to show the strong alarm warning text in the volume dialog -->
     <integer name="zen_mode_alarm_warning_threshold">5</integer>
 
+    <!-- Maximum number of optional conditions to display in the zen mode selection panel -->
+    <integer name="zen_mode_max_conditions">3</integer>
+
     <!-- Enable the default volume dialog -->
     <bool name="enable_volume_ui">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1143553..68a7622 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -197,8 +197,18 @@
     <!-- How far the expanded QS panel peeks from the header in collapsed state. -->
     <dimen name="qs_peek_height">8dp</dimen>
 
+    <!-- Zen mode panel: condition item button padding -->
     <dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
 
+    <!-- Zen mode panel: spacing between condition items -->
+    <dimen name="zen_mode_condition_detail_item_spacing">12dp</dimen>
+
+    <!-- Zen mode panel: spacing between two-line condition upper and lower lines -->
+    <dimen name="zen_mode_condition_detail_item_interline_spacing">4dp</dimen>
+
+    <!-- Zen mode panel: bottom padding, a bit less than qs_panel_padding -->
+    <dimen name="zen_mode_condition_detail_bottom_padding">4dp</dimen>
+
     <!-- used by DessertCase -->
     <dimen name="dessert_case_cell_size">192dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index b84b138..6ed24e0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume;
 
+import android.animation.LayoutTransition;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
@@ -36,6 +37,9 @@
 import android.util.MathUtils;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.CompoundButton;
 import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.ImageView;
@@ -65,7 +69,6 @@
     private static final int FOREVER_CONDITION_INDEX = 0;
     private static final int TIME_CONDITION_INDEX = 1;
     private static final int FIRST_CONDITION_INDEX = 2;
-    private static final long SELECT_DEFAULT_DELAY = 300;
 
     public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
 
@@ -76,6 +79,8 @@
     private final IconPulser mIconPulser;
     private final int mSubheadWarningColor;
     private final int mSubheadColor;
+    private final Interpolator mInterpolator;
+    private final int mMaxConditions;
 
     private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));
 
@@ -96,6 +101,7 @@
     private boolean mHidden = false;
     private int mSessionZen;
     private int mAttachedZen;
+    private boolean mAttached;
     private Condition mSessionExitCondition;
     private Condition[] mConditions;
     private Condition mTimeCondition;
@@ -109,6 +115,10 @@
         final Resources res = mContext.getResources();
         mSubheadWarningColor = res.getColor(R.color.system_warning_color);
         mSubheadColor = res.getColor(R.color.qs_subhead);
+        mInterpolator = AnimationUtils.loadInterpolator(mContext,
+                com.android.internal.R.interpolator.fast_out_slow_in);
+        mMaxConditions = MathUtils.constrain(res.getInteger(R.integer.zen_mode_max_conditions),
+                1, 100);
         if (DEBUG) Log.d(mTag, "new ZenModePanel");
     }
 
@@ -149,17 +159,30 @@
         Interaction.register(mMoreSettings, mInteractionCallback);
 
         mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
+        setLayoutTransition(newLayoutTransition());
+    }
+
+    private LayoutTransition newLayoutTransition() {
+        final LayoutTransition transition = new LayoutTransition();
+        transition.disableTransitionType(LayoutTransition.DISAPPEARING);
+        transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+        transition.setInterpolator(LayoutTransition.APPEARING, mInterpolator);
+        transition.setInterpolator(LayoutTransition.CHANGE_APPEARING, mInterpolator);
+        return transition;
     }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         if (DEBUG) Log.d(mTag, "onAttachedToWindow");
+        ((ViewGroup) getParent()).setLayoutTransition(newLayoutTransition());
+        mAttached = true;
         mAttachedZen = getSelectedZen(-1);
         mSessionZen = mAttachedZen;
         mSessionExitCondition = copy(mExitCondition);
         refreshExitConditionText();
         updateWidgets();
+        setRequestingConditions(!mHidden);
     }
 
     @Override
@@ -167,15 +190,19 @@
         super.onDetachedFromWindow();
         if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
         checkForAttachedZenChange();
+        mAttached = false;
         mAttachedZen = -1;
         mSessionZen = -1;
         mSessionExitCondition = null;
         setExpanded(false);
+        setRequestingConditions(false);
     }
 
     public void setHidden(boolean hidden) {
         if (mHidden == hidden) return;
+        if (DEBUG) Log.d(mTag, "hidden=" + hidden);
         mHidden = hidden;
+        setRequestingConditions(mAttached && !mHidden);
         updateWidgets();
     }
 
@@ -193,8 +220,10 @@
     private void setExpanded(boolean expanded) {
         if (expanded == mExpanded) return;
         mExpanded = expanded;
+        if (mExpanded) {
+            ensureSelection();
+        }
         updateWidgets();
-        setRequestingConditions(mExpanded);
         fireExpanded();
     }
 
@@ -331,10 +360,38 @@
     }
 
     private void handleUpdateConditions(Condition[] conditions) {
+        conditions = trimConditions(conditions);
+        if (Arrays.equals(conditions, mConditions)) {
+            final int count = mConditions == null ? 0 : mConditions.length;
+            if (DEBUG) Log.d(mTag, "handleUpdateConditions unchanged conditionCount=" + count);
+            return;
+        }
         mConditions = conditions;
         handleUpdateConditions();
     }
 
+    private Condition[] trimConditions(Condition[] conditions) {
+        if (conditions == null || conditions.length <= mMaxConditions) {
+            // no need to trim
+            return conditions;
+        }
+        // look for current exit condition, ensure it is included if found
+        int found = -1;
+        for (int i = 0; i < conditions.length; i++) {
+            final Condition c = conditions[i];
+            if (mSessionExitCondition != null && sameConditionId(mSessionExitCondition, c)) {
+                found = i;
+                break;
+            }
+        }
+        final Condition[] rt = Arrays.copyOf(conditions, mMaxConditions);
+        if (found >= mMaxConditions) {
+            // found after the first N, promote to the end of the first N
+            rt[mMaxConditions - 1] = conditions[found];
+        }
+        return rt;
+    }
+
     private void handleUpdateConditions() {
         final int conditionCount = mConditions == null ? 0 : mConditions.length;
         if (DEBUG) Log.d(mTag, "handleUpdateConditions conditionCount=" + conditionCount);
@@ -355,9 +412,10 @@
         if (isDowntime(mSessionExitCondition) && !foundDowntime) {
             bind(mSessionExitCondition, null);
         }
-        // ensure something is selected, after waiting for providers to respond
-        mHandler.removeMessages(H.SELECT_DEFAULT);
-        mHandler.sendEmptyMessageDelayed(H.SELECT_DEFAULT, SELECT_DEFAULT_DELAY);
+        // ensure something is selected
+        if (mExpanded) {
+            ensureSelection();
+        }
     }
 
     private static boolean isDowntime(Condition c) {
@@ -368,9 +426,9 @@
         return (ConditionTag) mZenConditions.getChildAt(index).getTag();
     }
 
-    private void handleSelectDefault() {
-        if (!mExpanded) return;
+    private void ensureSelection() {
         // are we left without anything selected?  if so, set a default
+        if (mZenConditions.getChildCount() == 0) return;
         for (int i = 0; i < mZenConditions.getChildCount(); i++) {
             if (getConditionTagAt(i).rb.isChecked()) {
                 if (DEBUG) Log.d(mTag, "Not selecting a default, checked="
@@ -419,7 +477,7 @@
         }
         tag.condition = condition;
         tag.rb.setEnabled(enabled);
-        if (mSessionExitCondition != null
+        if ((mSessionExitCondition != null || mAttachedZen != Global.ZEN_MODE_OFF)
                 && sameConditionId(mSessionExitCondition, tag.condition)) {
             tag.rb.setChecked(true);
         }
@@ -623,7 +681,6 @@
         private static final int UPDATE_CONDITIONS = 1;
         private static final int EXIT_CONDITION_CHANGED = 2;
         private static final int UPDATE_ZEN = 3;
-        private static final int SELECT_DEFAULT = 4;
 
         private H() {
             super(Looper.getMainLooper());
@@ -637,8 +694,6 @@
                 handleExitConditionChanged((Condition) msg.obj);
             } else if (msg.what == UPDATE_ZEN) {
                 handleUpdateZen(msg.arg1);
-            } else if (msg.what == SELECT_DEFAULT) {
-                handleSelectDefault();
             }
         }
     }