Implement new volume UI design.

- Add segmented zen-mode picker to the rocker UI.
- Add a new "no interruptions" value to the zen setting.
- Implement expandable condition subpanel on the rocker UI.
- Remove the old circle&slash icons.
- Suppress alarm sounds if in "no interruptions" mode.
- Add warning re: alarms to the condition UI.
- Allow rocker UI to display over the keyguard.
- Remove Notifications QS tile.
- Realign volume rocker to the top of the screen.
- Add support for new "days" sleepMode.
- New icon policy rules for "volume" slot.
- New important icon (star).

Associated Settings change:
  I6ed56791784968adfbd684f490dbbebed285a2dd

Bug:15831713
Change-Id: I35afe38646f04d2ba0dbac11c2c6356120a33694
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
deleted file mode 100644
index 3bdea79..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * 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.qs.tiles;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
-import android.view.ViewGroup;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.volume.VolumeComponent;
-import com.android.systemui.volume.VolumePanel;
-import com.android.systemui.volume.ZenModePanel;
-
-/** Quick settings tile: Notifications **/
-public class NotificationsTile extends QSTile<NotificationsTile.NotificationsState> {
-    private final ZenModeController mZenController;
-    private final AudioManager mAudioManager;
-
-    public NotificationsTile(Host host) {
-        super(host);
-        mZenController = host.getZenModeController();
-        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-    }
-
-    @Override
-    public DetailAdapter getDetailAdapter() {
-        return mDetailAdapter;
-    }
-
-    @Override
-    protected NotificationsState newTileState() {
-        return new NotificationsState();
-    }
-
-    @Override
-    public void setListening(boolean listening) {
-        if (listening) {
-            mZenController.addCallback(mCallback);
-            final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
-            mContext.registerReceiver(mReceiver, filter);
-        } else {
-            mZenController.removeCallback(mCallback);
-            mContext.unregisterReceiver(mReceiver);
-        }
-    }
-
-    @Override
-    protected void handleClick() {
-        showDetail(true);
-    }
-
-    @Override
-    protected void handleUpdateState(NotificationsState state, Object arg) {
-        state.visible = true;
-        state.zen = arg instanceof Boolean ? (Boolean) arg : mZenController.isZen();
-        state.ringerMode = mAudioManager.getRingerMode();
-        if (state.zen) {
-            state.iconId = R.drawable.ic_qs_zen_on;
-        } else if (state.ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
-            state.iconId = R.drawable.ic_qs_ringer_vibrate;
-        } else if (state.ringerMode == AudioManager.RINGER_MODE_SILENT) {
-            state.iconId = R.drawable.ic_qs_ringer_silent;
-        } else {
-            state.iconId = R.drawable.ic_qs_ringer_audible;
-        }
-        state.label = mContext.getString(R.string.quick_settings_notifications_label);
-    }
-
-    private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
-        @Override
-        public void onZenChanged(boolean zen) {
-            if (DEBUG) Log.d(TAG, "onZenChanged " + zen);
-            refreshState(zen);
-        }
-    };
-
-    public static final class NotificationsState extends QSTile.State {
-        public boolean zen;
-        public int ringerMode;
-
-        @Override
-        public boolean copyTo(State other) {
-            final NotificationsState o = (NotificationsState) other;
-            final boolean changed = o.zen != zen || o.ringerMode != ringerMode;
-            o.zen = zen;
-            o.ringerMode = ringerMode;
-            return super.copyTo(other) || changed;
-        }
-
-        @Override
-        protected StringBuilder toStringBuilder() {
-            final StringBuilder rt = super.toStringBuilder();
-            rt.insert(rt.length() - 1, ",zen=" + zen + ",ringerMode=" + ringerMode);
-            return rt;
-        }
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
-                refreshState();
-            }
-        }
-    };
-
-    private final DetailAdapter mDetailAdapter = new DetailAdapter() {
-
-        @Override
-        public int getTitle() {
-            return R.string.quick_settings_notifications_label;
-        }
-
-        @Override
-        public Boolean getToggleState() {
-            return null;
-        }
-
-        public void setToggleState(boolean state) {
-            // noop
-        }
-
-        public Intent getSettingsIntent() {
-            return ZenModePanel.ZEN_SETTINGS;
-        }
-
-        @Override
-        public View createDetailView(Context context, View convertView, ViewGroup parent) {
-            if (convertView != null) return convertView;
-            final VolumeComponent volumeComponent = mHost.getVolumeComponent();
-            final VolumePanel vp = new VolumePanel(mContext, parent, mZenController);
-            final View v = vp.getContentView();
-            v.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
-                @Override
-                public void onViewDetachedFromWindow(View v) {
-                    volumeComponent.setVolumePanel(null);
-                }
-
-                @Override
-                public void onViewAttachedToWindow(View v) {
-                    vp.updateStates();
-                    volumeComponent.setVolumePanel(vp);
-                }
-            });
-            vp.setZenModePanelCallback(new ZenModePanel.Callback() {
-                @Override
-                public void onMoreSettings() {
-                    mHost.startSettingsActivity(ZenModePanel.ZEN_SETTINGS);
-                }
-
-                @Override
-                public void onInteraction() {
-                    // noop
-                }
-            });
-            vp.postVolumeChanged(AudioManager.STREAM_RING, AudioManager.FLAG_SHOW_UI);
-            return v;
-        }
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 84eee24..5d1b16c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -266,7 +266,7 @@
                 final TextView summary = (TextView) item.findViewById(android.R.id.summary);
                 if (ap.isConnected) {
                     item.setMinimumHeight(mContext.getResources()
-                            .getDimensionPixelSize(R.dimen.qs_detail_item_height_connected));
+                            .getDimensionPixelSize(R.dimen.qs_detail_item_height_twoline));
                     summary.setText(R.string.quick_settings_connected);
                 } else {
                     summary.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 0a3fdef..a11e610 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -60,7 +60,7 @@
         } else if (mDemoMode && command.equals(COMMAND_STATUS)) {
             String volume = args.getString("volume");
             if (volume != null) {
-                int iconId = volume.equals("zen") ? R.drawable.stat_sys_ringer_zen
+                int iconId = volume.equals("important") ? R.drawable.stat_sys_zen_important
                         : volume.equals("silent") ? R.drawable.stat_sys_ringer_silent
                         : volume.equals("vibrate") ? R.drawable.stat_sys_ringer_vibrate
                         : 0;
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 f697098..a6b2110 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -738,6 +738,14 @@
         return mStatusBarView;
     }
 
+    @Override
+    protected void setZenMode(int mode) {
+        super.setZenMode(mode);
+        if (mIconPolicy != null) {
+            mIconPolicy.setZenMode(mode);
+        }
+    }
+
     private void startKeyguard() {
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index b6f5ae0..186b618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -24,6 +24,7 @@
 import android.content.IntentFilter;
 import android.media.AudioManager;
 import android.os.Handler;
+import android.provider.Settings.Global;
 import android.util.Log;
 
 import com.android.internal.telephony.IccCardConstants;
@@ -64,7 +65,7 @@
     private boolean mVolumeVisible;
 
     // zen mode
-    private boolean mZen;
+    private int mZen;
 
     // bluetooth device status
     private boolean mBluetoothEnabled = false;
@@ -155,7 +156,7 @@
         updateVolume();
     }
 
-    public void setZenMode(boolean zen) {
+    public void setZenMode(int zen) {
         mZen = zen;
         updateVolume();
     }
@@ -202,21 +203,21 @@
     private final void updateVolume() {
         AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         final int ringerMode = audioManager.getRingerMode();
-        final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
-                ringerMode == AudioManager.RINGER_MODE_VIBRATE ||
-                mZen;
-
-        final int iconId;
+        int iconId = 0;
         String contentDescription = null;
-        if (mZen) {
-            iconId = R.drawable.stat_sys_ringer_zen;
-            contentDescription = mContext.getString(R.string.zen_mode_title);
+        boolean visible = false;
+        if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
+            visible = true;
+            iconId = R.drawable.stat_sys_ringer_silent;
+            contentDescription = mContext.getString(R.string.accessibility_ringer_silent);
+        } else if (mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+            visible = true;
+            iconId = R.drawable.stat_sys_zen_important;
+            contentDescription = mContext.getString(R.string.zen_important_interruptions);
         } else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+            visible = true;
             iconId = R.drawable.stat_sys_ringer_vibrate;
             contentDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
-        } else {
-            iconId =  R.drawable.stat_sys_ringer_silent;
-            contentDescription = mContext.getString(R.string.accessibility_ringer_silent);
         }
 
         if (visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 04b1443..5fbade1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -21,19 +21,16 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 
-import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.tiles.AirplaneModeTile;
 import com.android.systemui.qs.tiles.BluetoothTile;
-import com.android.systemui.qs.tiles.BugreportTile;
 import com.android.systemui.qs.tiles.CastTile;
 import com.android.systemui.qs.tiles.CellularTile;
 import com.android.systemui.qs.tiles.ColorInversionTile;
 import com.android.systemui.qs.tiles.FlashlightTile;
-import com.android.systemui.qs.tiles.LocationTile;
-import com.android.systemui.qs.tiles.NotificationsTile;
-import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.qs.tiles.HotspotTile;
+import com.android.systemui.qs.tiles.LocationTile;
+import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.qs.tiles.WifiTile;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.BluetoothController;
@@ -93,7 +90,6 @@
         mTiles.add(new ColorInversionTile(this));
         mTiles.add(new CellularTile(this));
         mTiles.add(new AirplaneModeTile(this));
-        mTiles.add(new NotificationsTile(this));
         mTiles.add(new RotationLockTile(this));
         mTiles.add(new LocationTile(this));
         mTiles.add(new CastTile(this));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 4cf72a2..61902a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -22,14 +22,15 @@
 public interface ZenModeController {
     void addCallback(Callback callback);
     void removeCallback(Callback callback);
-    void setZen(boolean zen);
-    boolean isZen();
+    void setZen(int zen);
+    int getZen();
     void requestConditions(boolean request);
     void setExitConditionId(Uri exitConditionId);
     Uri getExitConditionId();
+    boolean hasNextAlarm();
 
     public static class Callback {
-        public void onZenChanged(boolean zen) {}
+        public void onZenChanged(int zen) {}
         public void onExitConditionChanged(Uri exitConditionId) {}
         public void onConditionsChanged(Condition[] conditions) {}
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index da8fd32..7703966 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -28,6 +28,7 @@
 import android.service.notification.ZenModeConfig;
 import android.util.Slog;
 
+import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.qs.GlobalSetting;
 
 import java.util.ArrayList;
@@ -44,15 +45,17 @@
     private final GlobalSetting mConfigSetting;
     private final INotificationManager mNoMan;
     private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>();
+    private final LockPatternUtils mUtils;
 
     private boolean mRequesting;
 
     public ZenModeControllerImpl(Context context, Handler handler) {
         mContext = context;
+        mUtils = new LockPatternUtils(mContext);
         mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
             @Override
             protected void handleValueChanged(int value) {
-                fireZenChanged(value != 0);
+                fireZenChanged(value);
             }
         };
         mConfigSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE_CONFIG_ETAG) {
@@ -78,13 +81,13 @@
     }
 
     @Override
-    public boolean isZen() {
-        return mModeSetting.getValue() != 0;
+    public int getZen() {
+        return mModeSetting.getValue();
     }
 
     @Override
-    public void setZen(boolean zen) {
-        mModeSetting.setValue(zen ? 1 : 0);
+    public void setZen(int zen) {
+        mModeSetting.setValue(zen);
     }
 
     @Override
@@ -122,7 +125,12 @@
         return null;
     }
 
-    private void fireZenChanged(boolean zen) {
+    @Override
+    public boolean hasNextAlarm() {
+        return mUtils.getNextAlarm() != null;
+    }
+
+    private void fireZenChanged(int zen) {
         for (Callback cb : mCallbacks) {
             cb.onZenChanged(zen);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
new file mode 100644
index 0000000..d339dd4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -0,0 +1,93 @@
+/*
+ * 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.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+import java.util.Objects;
+
+public class SegmentedButtons extends LinearLayout {
+
+    private final Context mContext;
+    private final LayoutInflater mInflater;
+
+    private Callback mCallback;
+    private Object mSelectedValue;
+
+    public SegmentedButtons(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        mInflater = LayoutInflater.from(mContext);
+        setOrientation(HORIZONTAL);
+    }
+
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    public Object getSelectedValue() {
+        return mSelectedValue;
+    }
+
+    public void setSelectedValue(Object value) {
+        if (Objects.equals(value, mSelectedValue)) return;
+        mSelectedValue = value;
+        for (int i = 0; i < getChildCount(); i++) {
+            final View c = getChildAt(i);
+            final Object tag = c.getTag();
+            c.setSelected(Objects.equals(mSelectedValue, tag));
+        }
+        fireOnSelected();
+    }
+
+    public void addButton(int labelResId, Object value) {
+        final Button b = (Button) mInflater.inflate(R.layout.segmented_button, this, false);
+        b.setText(labelResId);
+        final LayoutParams lp = (LayoutParams) b.getLayoutParams();
+        if (getChildCount() == 0) {
+            lp.leftMargin = lp.rightMargin = 0; // first button has no margin
+        }
+        b.setLayoutParams(lp);
+        addView(b);
+        b.setTag(value);
+        b.setOnClickListener(mClick);
+    }
+
+    private void fireOnSelected() {
+        if (mCallback != null) {
+            mCallback.onSelected(mSelectedValue);
+        }
+    }
+
+    private final View.OnClickListener mClick = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            setSelectedValue(v.getTag());
+        }
+    };
+
+    public interface 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 53daaae..b6d6767 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.volume;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.BroadcastReceiver;
@@ -38,24 +36,21 @@
 import android.media.session.MediaController;
 import android.media.session.MediaController.VolumeInfo;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Vibrator;
+import android.provider.Settings.Global;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -71,7 +66,8 @@
  * @hide
  */
 public class VolumePanel extends Handler {
-    private static boolean LOGD = false;
+    private static final String TAG = "VolumePanel";
+    private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
 
@@ -89,7 +85,6 @@
     private static final int FREE_DELAY = 10000;
     private static final int TIMEOUT_DELAY = 3000;
     private static final int TIMEOUT_DELAY_EXPANDED = 10000;
-    private static final float ICON_PULSE_SCALE = 1.3f;
 
     private static final int MSG_VOLUME_CHANGED = 0;
     private static final int MSG_FREE_RESOURCES = 1;
@@ -105,6 +100,7 @@
     private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
     private static final int MSG_LAYOUT_DIRECTION = 12;
     private static final int MSG_ZEN_MODE_CHANGED = 13;
+    private static final int MSG_USER_ACTIVITY = 14;
 
     // Pseudo stream type for master volume
     private static final int STREAM_MASTER = -100;
@@ -114,7 +110,6 @@
     protected final Context mContext;
     private final AudioManager mAudioManager;
     private final ZenModeController mZenController;
-    private final Interpolator mFastOutSlowInInterpolator;
     private boolean mRingIsSilent;
     private boolean mVoiceCapable;
     private boolean mZenModeCapable;
@@ -135,18 +130,12 @@
     private final ViewGroup mPanel;
     /** Contains the slider and its touchable icons */
     private final ViewGroup mSliderPanel;
-    /** The button that expands the dialog to show the zen panel */
-    private final ImageView mExpandButton;
-    /** Dummy divider icon that needs to vanish with the expand button */
-    private final View mExpandDivider;
     /** The zen mode configuration panel view stub */
     private final ViewStub mZenPanelStub;
     /** The zen mode configuration panel view, once inflated */
     private ZenModePanel mZenPanel;
-    /** Dummy divider icon that needs to vanish with the zen panel */
-    private final View mZenPanelDivider;
 
-    private ZenModePanel.Callback mZenPanelCallback;
+    private Callback mCallback;
 
     /** Currently active stream that shows up at the top of the list of sliders */
     private int mActiveStreamType = -1;
@@ -231,7 +220,6 @@
         ViewGroup group;
         ImageView icon;
         SeekBar seekbarView;
-        View seekbarContainer;
         int iconRes;
         int iconMuteRes;
     }
@@ -280,13 +268,11 @@
 
 
     public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
-        mTag = String.format("VolumePanel%s.%08x", parent == null ? "Dialog" : "", hashCode());
+        mTag = String.format("%s.%s.%08x", TAG, parent == null ? "Dialog" : "Embed", hashCode());
         mContext = context;
         mParent = parent;
         mZenController = zenController;
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.fast_out_slow_in);
 
         // For now, only show master volume if master volume is supported
         final Resources res = context.getResources();
@@ -318,12 +304,11 @@
             lp.token = null;
             // Offset from the top
             lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
-            lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_width);
-            lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
+            lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
             lp.format = PixelFormat.TRANSLUCENT;
-            lp.windowAnimations = R.style.Animation_VolumePanel;
+            lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
+            lp.gravity = Gravity.TOP;
             window.setAttributes(lp);
-            window.setGravity(Gravity.TOP);
             window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
             window.requestFeature(Window.FEATURE_NO_TITLE);
             window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
@@ -337,7 +322,7 @@
                 public void onDismiss(DialogInterface dialog) {
                     mActiveStreamType = -1;
                     mAudioManager.forceVolumeControlStream(mActiveStreamType);
-                    collapse();
+                    setZenPanelVisible(false);
                 }
             });
 
@@ -362,19 +347,14 @@
         }
         mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
         mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
-        mExpandButton = (ImageView) mView.findViewById(com.android.systemui.R.id.expand_button);
-        mExpandDivider = mView.findViewById(com.android.systemui.R.id.expand_button_divider);
         mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
-        mZenPanelDivider = mView.findViewById(com.android.systemui.R.id.zen_panel_divider);
 
         mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
         mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
 
         mZenModeCapable = !useMasterVolume && mZenController != null;
-        mZenPanelDivider.setVisibility(View.GONE);
-        mExpandButton.setOnClickListener(mClickListener);
-        updateZenMode(mZenController == null ? false : mZenController.isZen());
+        updateZenMode(mZenController != null ? mZenController.getZen() : Global.ZEN_MODE_OFF);
         mZenController.addCallback(mZenCallback);
 
         final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
@@ -502,17 +482,7 @@
                         toggle(sc);
                     }
                 });
-                sc.icon.setOnLongClickListener(new OnLongClickListener() {
-                    @Override
-                    public boolean onLongClick(View v) {
-                        resetTimeout();
-                        longToggle(sc);
-                        return true;
-                    }
-                });
             }
-            sc.seekbarContainer =
-                    sc.group.findViewById(com.android.systemui.R.id.seekbar_container);
             sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
             final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
                     streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
@@ -533,29 +503,19 @@
         }
     }
 
-    private void longToggle(StreamControl sc) {
-        if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
-            mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
-            postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
-        } else {
-            mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
-            postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI); // disable the slider
-        }
-    }
-
     private void reorderSliders(int activeStreamType) {
         mSliderPanel.removeAllViews();
 
         final StreamControl active = mStreamControls.get(activeStreamType);
         if (active == null) {
-            Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
+            Log.e(TAG, "Missing stream type! - " + activeStreamType);
             mActiveStreamType = -1;
         } else {
             mSliderPanel.addView(active.group);
             mActiveStreamType = activeStreamType;
             active.group.setVisibility(View.VISIBLE);
             updateSlider(active);
-            updateZenMode(mZenController == null ? false : mZenController.isZen());
+            setZenPanelVisible(isNotificationOrRing(mActiveStreamType));
         }
     }
 
@@ -575,6 +535,7 @@
 
     private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
         final boolean wasEnabled = sc.seekbarView.isEnabled();
+        final boolean isRinger = isNotificationOrRing(sc.streamType);
         if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
             // never disable touch interactions for remote playback, the muting is not tied to
             // the state of the phone.
@@ -583,40 +544,37 @@
                         (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
                         (sConfirmSafeVolumeDialog != null)) {
             sc.seekbarView.setEnabled(false);
-        } else if (isNotificationOrRing(sc.streamType)
-                && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
+        } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
             sc.seekbarView.setEnabled(false);
+            sc.icon.setEnabled(false);
+            sc.icon.setClickable(false);
         } else {
             sc.seekbarView.setEnabled(true);
+            sc.icon.setEnabled(true);
         }
-        // pulse the ringer icon when the disabled slider is touched in silent mode
-        if (sc.icon.isClickable() && wasEnabled != sc.seekbarView.isEnabled()) {
+        // show the silent hint when the disabled slider is touched in silent mode
+        if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
             if (sc.seekbarView.isEnabled()) {
-                sc.seekbarContainer.setOnTouchListener(null);
+                sc.group.setOnTouchListener(null);
+                sc.icon.setClickable(true);
             } else {
-                sc.seekbarContainer.setOnTouchListener(new View.OnTouchListener() {
+                final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
                     @Override
                     public boolean onTouch(View v, MotionEvent event) {
                         resetTimeout();
-                        pulseIcon(sc.icon);
+                        showSilentHint();
                         return false;
                     }
-                });
+                };
+                sc.group.setOnTouchListener(showHintOnTouch);
             }
         }
     }
 
-    private void pulseIcon(final ImageView icon) {
-        if (icon.getScaleX() != 1) return;  // already running
-        icon.animate().cancel();
-        icon.animate().scaleX(ICON_PULSE_SCALE).scaleY(ICON_PULSE_SCALE)
-                .setInterpolator(mFastOutSlowInInterpolator)
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        icon.animate().scaleX(1).scaleY(1).setListener(null);
-                    }
-                });
+    private void showSilentHint() {
+        if (mZenPanel != null) {
+            mZenPanel.showSilentHint();
+        }
     }
 
     private static boolean isNotificationOrRing(int streamType) {
@@ -624,47 +582,45 @@
                 || streamType == AudioManager.STREAM_NOTIFICATION;
     }
 
-    public void setZenModePanelCallback(ZenModePanel.Callback callback) {
-        mZenPanelCallback = callback;
+    public void setCallback(Callback callback) {
+        mCallback = callback;
     }
 
-    private void expand() {
-        if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
-        if (mZenPanel == null) {
-            mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
-            final boolean isDialog = mDialog != null;
-            mZenPanel.init(mZenController, isDialog ? 'D' : 'E', isDialog);
-            mZenPanel.setCallback(new ZenModePanel.Callback() {
-                @Override
-                public void onMoreSettings() {
-                    if (mZenPanelCallback != null) {
-                        mZenPanelCallback.onMoreSettings();
+    private void setZenPanelVisible(boolean visible) {
+        if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
+        if (visible) {
+            if (mZenPanel == null) {
+                mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
+                mZenPanel.init(mZenController, mDialog != null ? 'D' : 'E');
+                mZenPanel.setCallback(new ZenModePanel.Callback() {
+                    @Override
+                    public void onMoreSettings() {
+                        if (mCallback != null) {
+                            mCallback.onZenSettings();
+                        }
                     }
-                }
 
-                @Override
-                public void onInteraction() {
-                    resetTimeout();
-                    if (mZenPanelCallback != null) {
-                        mZenPanelCallback.onInteraction();
+                    @Override
+                    public void onInteraction() {
+                        resetTimeout();
                     }
-                }
-            });
-        }
-        mZenPanel.setVisibility(View.VISIBLE);
-        mZenPanelDivider.setVisibility(View.VISIBLE);
-        mTimeoutDelay = TIMEOUT_DELAY_EXPANDED;
-        resetTimeout();
-    }
 
-    private void collapse() {
-        if (LOGD) Log.d(mTag, "collapse mZenPanel=" + mZenPanel);
-        if (mZenPanel != null) {
-            mZenPanel.setVisibility(View.GONE);
+                    @Override
+                    public void onExpanded(boolean expanded) {
+                        mTimeoutDelay = expanded ? TIMEOUT_DELAY_EXPANDED : TIMEOUT_DELAY;
+                        resetTimeout();
+                    }
+                });
+            }
+            mZenPanel.setVisibility(View.VISIBLE);
+            resetTimeout();
+        } else {
+            if (mZenPanel != null) {
+                mZenPanel.setVisibility(View.GONE);
+            }
+            mTimeoutDelay = TIMEOUT_DELAY;
+            resetTimeout();
         }
-        mZenPanelDivider.setVisibility(View.GONE);
-        mTimeoutDelay = TIMEOUT_DELAY;
-        resetTimeout();
     }
 
     public void updateStates() {
@@ -675,25 +631,14 @@
         }
     }
 
-    private void updateZenMode(boolean zen) {
-        if (mZenModeCapable) {
-            final boolean show = isNotificationOrRing(mActiveStreamType);
-            mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
-            mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
-            mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
-                    : com.android.systemui.R.drawable.ic_vol_zen_off);
-            if (show && !zen) {
-                collapse();
-            }
-        } else {
-            mExpandButton.setVisibility(View.GONE);
-            mExpandDivider.setVisibility(View.GONE);
-        }
+    private void updateZenMode(int zen) {
+        final boolean show = mZenModeCapable && isNotificationOrRing(mActiveStreamType);
+        setZenPanelVisible(show);
     }
 
-    public void postZenModeChanged(boolean zen) {
+    public void postZenModeChanged(int zen) {
         removeMessages(MSG_ZEN_MODE_CHANGED);
-        obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0, 0).sendToTarget();
+        obtainMessage(MSG_ZEN_MODE_CHANGED, zen).sendToTarget();
     }
 
     public void postVolumeChanged(int streamType, int flags) {
@@ -955,8 +900,8 @@
         }
 
         // Pulse the slider icon if an adjustment was suppressed due to silent mode.
-        if (sc != null && (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
-            pulseIcon(sc.icon);
+        if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
+            showSilentHint();
         }
     }
 
@@ -1233,15 +1178,23 @@
                 break;
 
             case MSG_ZEN_MODE_CHANGED:
-                updateZenMode(msg.arg1 != 0);
+                updateZenMode(msg.arg1);
+                break;
+
+            case MSG_USER_ACTIVITY:
+                if (mCallback != null) {
+                    mCallback.onInteraction();
+                }
                 break;
         }
     }
 
-    public void resetTimeout() {
+    private void resetTimeout() {
         if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
         removeMessages(MSG_TIMEOUT);
         sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
+        removeMessages(MSG_USER_ACTIVITY);
+        sendEmptyMessage(MSG_USER_ACTIVITY);
     }
 
     private void forceTimeout() {
@@ -1273,29 +1226,8 @@
         }
     };
 
-    private final View.OnClickListener mClickListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            if (v == mExpandButton && mZenController != null) {
-                final boolean newZen = !mZenController.isZen();
-                AsyncTask.execute(new Runnable() {
-                    @Override
-                    public void run() {
-                        mZenController.setZen(newZen);
-                    }
-                });
-                if (newZen) {
-                    expand();
-                } else {
-                    collapse();
-                }
-            }
-            resetTimeout();
-        }
-    };
-
     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
-        public void onZenChanged(boolean zen) {
+        public void onZenChanged(int zen) {
             postZenModeChanged(zen);
         }
     };
@@ -1305,4 +1237,9 @@
             onRemoteVolumeUpdateIfShown();
         }
     };
+
+    public interface Callback {
+        void onZenSettings();
+        void onInteraction();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index c1f92ff..51be833 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -1,8 +1,8 @@
 package com.android.systemui.volume;
 
+import android.app.ActivityManagerNative;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioManager;
 import android.media.IRemoteVolumeController;
@@ -17,8 +17,8 @@
 import android.provider.Settings;
 import android.util.Log;
 
-import com.android.systemui.R;
 import com.android.systemui.SystemUI;
+import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 
@@ -80,17 +80,19 @@
 
     private void initPanel() {
         mPanel = new VolumePanel(mContext, null, new ZenModeControllerImpl(mContext, mHandler));
-        final int delay = mContext.getResources().getInteger(R.integer.feedback_start_delay);
-        mPanel.setZenModePanelCallback(new ZenModePanel.Callback() {
+        mPanel.setCallback(new VolumePanel.Callback() {
             @Override
-            public void onMoreSettings() {
+            public void onZenSettings() {
                 mHandler.removeCallbacks(mStartZenSettings);
-                mHandler.postDelayed(mStartZenSettings, delay);
+                mHandler.post(mStartZenSettings);
             }
 
             @Override
             public void onInteraction() {
-                mDialogPanel.resetTimeout();
+                final KeyguardViewMediator kvm = getComponent(KeyguardViewMediator.class);
+                if (kvm != null) {
+                    kvm.userActivity();
+                }
             }
         });
         mDialogPanel = mPanel;
@@ -108,6 +110,11 @@
         @Override
         public void run() {
             mDialogPanel.postDismiss();
+            try {
+                // Dismiss the lock screen when Settings starts.
+                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+            } catch (RemoteException e) {
+            }
             final Intent intent = ZenModePanel.ZEN_SETTINGS;
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
             mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 9917944..afa5bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.volume;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -25,6 +27,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
 import android.util.AttributeSet;
@@ -32,7 +35,8 @@
 import android.view.ContextThemeWrapper;
 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;
@@ -47,12 +51,19 @@
 import java.util.Objects;
 
 public class ZenModePanel extends LinearLayout {
-    private static final boolean DEBUG = false;
+    private static final String TAG = "ZenModePanel";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final int[] MINUTE_BUCKETS = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
+    private static final int[] MINUTE_BUCKETS = DEBUG
+            ? new int[] { 1, 2, 5, 15, 30, 45, 60, 120, 180, 240, 480 }
+            : new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
     private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
     private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
     private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
+    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 float SILENT_HINT_PULSE_SCALE = 1.1f;
 
     private static final int SECONDS_MS = 1000;
     private static final int MINUTES_MS = 60 * SECONDS_MS;
@@ -63,73 +74,110 @@
     private final LayoutInflater mInflater;
     private final H mHandler = new H();
     private final Favorites mFavorites;
+    private final Interpolator mFastOutSlowInInterpolator;
 
     private char mLogTag = '?';
     private String mTag;
-    private LinearLayout mConditions;
+
+    private SegmentedButtons mZenButtons;
+    private View mZenSubhead;
+    private TextView mZenSubheadCollapsed;
+    private TextView mZenSubheadExpanded;
     private View mMoreSettings;
+    private LinearLayout mZenConditions;
+    private View mAlarmWarning;
+
     private Callback mCallback;
     private ZenModeController mController;
     private boolean mRequestingConditions;
     private Uri mExitConditionId;
     private int mBucketIndex = -1;
-    private boolean mShowing;
+    private boolean mExpanded;
+    private int mAttachedZen;
+    private String mExitConditionText;
 
     public ZenModePanel(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
         mFavorites = new Favorites();
         mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
+                android.R.interpolator.fast_out_slow_in);
         updateTag();
         if (DEBUG) Log.d(mTag, "new ZenModePanel");
     }
 
     private void updateTag() {
-        mTag = "ZenModePanel/" + mLogTag + "/" + Integer.toHexString(System.identityHashCode(this));
+        mTag = TAG + "/" + mLogTag + "/" + Integer.toHexString(System.identityHashCode(this));
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mConditions = (LinearLayout) findViewById(android.R.id.content);
-        mMoreSettings = findViewById(android.R.id.button2);
+
+        mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
+        mZenButtons.addButton(R.string.interruption_level_none, Global.ZEN_MODE_NO_INTERRUPTIONS);
+        mZenButtons.addButton(R.string.interruption_level_priority,
+                Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        mZenButtons.addButton(R.string.interruption_level_all, Global.ZEN_MODE_OFF);
+        mZenButtons.setCallback(mZenButtonsCallback);
+
+        mZenSubhead = findViewById(R.id.zen_subhead);
+
+        mZenSubheadCollapsed = (TextView) findViewById(R.id.zen_subhead_collapsed);
+        mZenSubheadCollapsed.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                setExpanded(true);
+                fireInteraction();
+            }
+        });
+
+        mZenSubheadExpanded = (TextView) findViewById(R.id.zen_subhead_expanded);
+        mZenSubheadExpanded.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                setExpanded(false);
+                fireInteraction();
+            }
+        });
+
+        mMoreSettings = findViewById(R.id.zen_more_settings);
         mMoreSettings.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 fireMoreSettings();
+                fireInteraction();
             }
         });
+
+        mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
+
+        mAlarmWarning = findViewById(R.id.zen_alarm_warning);
     }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         if (DEBUG) Log.d(mTag, "onAttachedToWindow");
-        final ViewGroup p = (ViewGroup) getParent();
-        updateShowing(p != null && p.getVisibility() == VISIBLE);
+        mAttachedZen = getSelectedZen(-1);
+        refreshExitConditionText();
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
-        updateShowing(false);
+        mAttachedZen = -1;
+        setExpanded(false);
     }
 
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-        final boolean vis = visibility == VISIBLE;
-        updateShowing(isAttachedToWindow() && vis);
-    }
-
-    private void updateShowing(boolean showing) {
-        if (showing == mShowing) return;
-        mShowing = showing;
-        if (DEBUG) Log.d(mTag, "mShowing=" + mShowing);
-        if (mController != null) {
-            setRequestingConditions(mShowing);
-        }
+    private void setExpanded(boolean expanded) {
+        if (expanded == mExpanded) return;
+        mExpanded = expanded;
+        updateWidgets();
+        setRequestingConditions(mExpanded);
+        fireExpanded();
     }
 
     /** Start or stop requesting relevant zen mode exit conditions */
@@ -149,24 +197,40 @@
                 timeCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
             }
             if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);
-            bind(timeCondition, mConditions.getChildAt(0));
-            handleUpdateConditions(new Condition[0]);
+            handleUpdateConditions(new Condition[0]);  // ensures forever exists
+            bind(timeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
+            checkForDefault();
         } else {
-            mConditions.removeAllViews();
+            mZenConditions.removeAllViews();
         }
     }
 
-    public void init(ZenModeController controller, char logTag, boolean moreSettings) {
+    public void init(ZenModeController controller, char logTag) {
         mController = controller;
         mLogTag = logTag;
         updateTag();
-        mExitConditionId = mController.getExitConditionId();
+        setExitConditionId(mController.getExitConditionId());
+        refreshExitConditionText();
+        mAttachedZen = getSelectedZen(-1);
+        handleUpdateZen(mController.getZen());
         if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId);
-        mMoreSettings.setVisibility(moreSettings ? VISIBLE : GONE);
-        mConditions.removeAllViews();
+        mZenConditions.removeAllViews();
         mController.addCallback(mZenCallback);
-        if (mShowing) {
-            setRequestingConditions(true);
+    }
+
+    private void setExitConditionId(Uri exitConditionId) {
+        if (Objects.equals(mExitConditionId, exitConditionId)) return;
+        mExitConditionId = exitConditionId;
+        refreshExitConditionText();
+    }
+
+    private void refreshExitConditionText() {
+        if (mExitConditionId == null) {
+            mExitConditionText = mContext.getString(R.string.zen_mode_forever);
+        } else if (ZenModeConfig.isValidCountdownConditionId(mExitConditionId)) {
+            mExitConditionText = parseExistingTimeCondition(mExitConditionId).summary;
+        } else {
+            mExitConditionText = "(until condition ends)";  // TODO persist current description
         }
     }
 
@@ -174,6 +238,59 @@
         mCallback = callback;
     }
 
+    public void showSilentHint() {
+        if (DEBUG) Log.d(mTag, "showSilentHint");
+        if (mZenButtons == null || mZenButtons.getChildCount() == 0) return;
+        final View noneButton = mZenButtons.getChildAt(0);
+        if (noneButton.getScaleX() != 1) return;  // already running
+        noneButton.animate().cancel();
+        noneButton.animate().scaleX(SILENT_HINT_PULSE_SCALE).scaleY(SILENT_HINT_PULSE_SCALE)
+                .setInterpolator(mFastOutSlowInInterpolator)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        noneButton.animate().scaleX(1).scaleY(1).setListener(null);
+                    }
+                });
+    }
+
+    private void handleUpdateZen(int zen) {
+        if (mAttachedZen != -1 && mAttachedZen != zen) {
+            setExpanded(zen != Global.ZEN_MODE_OFF);
+            mAttachedZen = zen;
+        }
+        mZenButtons.setSelectedValue(zen);
+        updateWidgets();
+    }
+
+    private int getSelectedZen(int defValue) {
+        final Object zen = mZenButtons.getSelectedValue();
+        return zen != null ? (Integer) zen : defValue;
+    }
+
+    private void updateWidgets() {
+        final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
+        final boolean zenOff = zen == Global.ZEN_MODE_OFF;
+        final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+        mZenSubhead.setVisibility(!zenOff ? VISIBLE : GONE);
+        mZenSubheadExpanded.setVisibility(mExpanded ? VISIBLE : GONE);
+        mZenSubheadCollapsed.setVisibility(!mExpanded ? VISIBLE : GONE);
+        mMoreSettings.setVisibility(zenImportant && mExpanded ? VISIBLE : GONE);
+        mZenConditions.setVisibility(!zenOff && mExpanded ? VISIBLE : GONE);
+        mAlarmWarning.setVisibility(zenNone && mExpanded && mController != null
+                && mController.hasNextAlarm() ? VISIBLE : GONE);
+
+        if (zenNone) {
+            mZenSubheadExpanded.setText(R.string.zen_no_interruptions);
+            mZenSubheadCollapsed.setText(mExitConditionText);
+        } else if (zenImportant) {
+            mZenSubheadExpanded.setText(R.string.zen_important_interruptions);
+            mZenSubheadCollapsed.setText(mExitConditionText);
+        }
+    }
+
     private Condition parseExistingTimeCondition(Uri conditionId) {
         final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
         if (time == 0) return null;
@@ -200,23 +317,23 @@
 
     private void handleUpdateConditions(Condition[] conditions) {
         final int newCount = conditions == null ? 0 : conditions.length;
-        for (int i = mConditions.getChildCount() - 1; i > newCount; i--) {
-            mConditions.removeViewAt(i);
+        if (DEBUG) Log.d(mTag, "handleUpdateConditions newCount=" + newCount);
+        for (int i = mZenConditions.getChildCount(); i >= newCount + FIRST_CONDITION_INDEX; i--) {
+            mZenConditions.removeViewAt(i);
         }
+        bind(null, mZenConditions.getChildAt(FOREVER_CONDITION_INDEX));
         for (int i = 0; i < newCount; i++) {
-            bind(conditions[i], mConditions.getChildAt(i + 1));
+            bind(conditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i));
         }
-        bind(null, mConditions.getChildAt(newCount + 1));
-        checkForDefault();
     }
 
     private ConditionTag getConditionTagAt(int index) {
-        return (ConditionTag) mConditions.getChildAt(index).getTag();
+        return (ConditionTag) mZenConditions.getChildAt(index).getTag();
     }
 
     private void checkForDefault() {
         // are we left without anything selected?  if so, set a default
-        for (int i = 0; i < mConditions.getChildCount(); i++) {
+        for (int i = 0; i < mZenConditions.getChildCount(); i++) {
             if (getConditionTagAt(i).rb.isChecked()) {
                 return;
             }
@@ -224,19 +341,19 @@
         if (DEBUG) Log.d(mTag, "Selecting a default");
         final int favoriteIndex = mFavorites.getMinuteIndex();
         if (favoriteIndex == -1) {
-            getConditionTagAt(mConditions.getChildCount() - 1).rb.setChecked(true);
+            getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
         } else {
             final Condition c = newTimeCondition(MINUTE_BUCKETS[favoriteIndex]);
             mBucketIndex = favoriteIndex;
-            bind(c, mConditions.getChildAt(0));
-            getConditionTagAt(0).rb.setChecked(true);
+            bind(c, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
+            getConditionTagAt(TIME_CONDITION_INDEX).rb.setChecked(true);
         }
     }
 
     private void handleExitConditionChanged(Uri exitCondition) {
-        mExitConditionId = exitCondition;
+        setExitConditionId(exitCondition);
         if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitConditionId);
-        final int N = mConditions.getChildCount();
+        final int N = mZenConditions.getChildCount();
         for (int i = 0; i < N; i++) {
             final ConditionTag tag = getConditionTagAt(i);
             tag.rb.setChecked(Objects.equals(tag.conditionId, exitCondition));
@@ -249,7 +366,7 @@
         if (convertView == null) {
             row = mInflater.inflate(R.layout.zen_mode_condition, this, false);
             if (DEBUG) Log.d(mTag, "Adding new condition view for: " + condition);
-            mConditions.addView(row);
+            mZenConditions.addView(row);
         } else {
             row = convertView;
         }
@@ -264,9 +381,9 @@
         tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                if (mShowing && isChecked) {
+                if (mExpanded && isChecked) {
                     if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.conditionId);
-                    final int N = mConditions.getChildCount();
+                    final int N = mZenConditions.getChildCount();
                     for (int i = 0; i < N; i++) {
                         ConditionTag childTag = getConditionTagAt(i);
                         if (childTag == tag) continue;
@@ -284,7 +401,7 @@
             title.setText(condition.summary);
         }
         title.setEnabled(enabled);
-        title.setAlpha(enabled ? 1 : .5f);
+        title.setAlpha(enabled ? 1 : .4f);
         final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
         button1.setOnClickListener(new OnClickListener() {
             @Override
@@ -365,7 +482,7 @@
         if (mController != null) {
             mController.setExitConditionId(conditionId);
         }
-        mExitConditionId = conditionId;
+        setExitConditionId(conditionId);
         if (conditionId == null) {
             mFavorites.setMinuteIndex(-1);
         } else if (ZenModeConfig.isValidCountdownConditionId(conditionId) && mBucketIndex != -1) {
@@ -385,8 +502,18 @@
         }
     }
 
+    private void fireExpanded() {
+        if (mCallback != null) {
+            mCallback.onExpanded(mExpanded);
+        }
+    }
+
     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
         @Override
+        public void onZenChanged(int zen) {
+            mHandler.obtainMessage(H.UPDATE_ZEN, zen, 0).sendToTarget();
+        }
+        @Override
         public void onConditionsChanged(Condition[] conditions) {
             mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
         }
@@ -400,6 +527,7 @@
     private final class H extends Handler {
         private static final int UPDATE_CONDITIONS = 1;
         private static final int EXIT_CONDITION_CHANGED = 2;
+        private static final int UPDATE_ZEN = 3;
 
         private H() {
             super(Looper.getMainLooper());
@@ -409,8 +537,11 @@
         public void handleMessage(Message msg) {
             if (msg.what == UPDATE_CONDITIONS) {
                 handleUpdateConditions((Condition[])msg.obj);
+                checkForDefault();
             } else if (msg.what == EXIT_CONDITION_CHANGED) {
                 handleExitConditionChanged((Uri)msg.obj);
+            } else if (msg.what == UPDATE_ZEN) {
+                handleUpdateZen(msg.arg1);
             }
         }
     }
@@ -418,6 +549,7 @@
     public interface Callback {
         void onMoreSettings();
         void onInteraction();
+        void onExpanded(boolean expanded);
     }
 
     // used as the view tag on condition rows
@@ -466,4 +598,15 @@
             return Math.max(-1, Math.min(MINUTE_BUCKETS.length - 1, index));
         }
     }
+
+    private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
+        @Override
+        public void onSelected(Object value) {
+            if (value != null && mZenButtons.isShown()) {
+                if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + value);
+                mController.setZen((Integer) value);
+                mController.setExitConditionId(null);
+            }
+        }
+    };
 }