QS: Wifi control panel.

Create first example of an inline detail view with line
items, bind to new access-point controller.

Move the common bits of all control panels into a
common abstraction.  Share common parts of layout
(More settings / done button).

Change-Id: I14a1bf548f3291dc628f97bf09dd4e38ac2fb2dc
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index e1c460c..ca46437 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -17,41 +17,36 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@color/system_primary_color" >
-
-    <ImageView
-        android:id="@android:id/button1"
-        android:layout_width="64dp"
-        android:layout_height="64dp"
-        android:layout_alignParentStart="true"
-        android:contentDescription="@string/accessibility_quick_settings_close"
-        android:padding="@dimen/qs_panel_padding"
-        android:src="@drawable/ic_qs_back" />
+    android:background="@color/system_primary_color"
+    android:padding="16dp" >
 
     <TextView
-        android:id="@android:id/title"
-        android:layout_width="match_parent"
-        android:layout_height="64dp"
-        android:layout_alignParentTop="true"
-        android:layout_toEndOf="@android:id/button1"
-        android:layout_toStartOf="@android:id/checkbox"
-        android:gravity="center_vertical"
-        android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
-
-    <ImageView
-        android:id="@android:id/custom"
-        android:layout_width="match_parent"
+        android:id="@android:id/button1"
+        style="@style/QSBorderlessButton"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_below="@android:id/title"
-        android:layout_marginLeft="16dip"
-        android:layout_marginRight="16dip"
-        android:scaleType="fitXY"
-        android:src="?android:attr/dividerHorizontal" />
+        android:minWidth="88dp"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:text="@string/quick_settings_done"
+        android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+
+    <TextView
+        android:id="@android:id/button2"
+        style="@style/QSBorderlessButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_marginEnd="8dp"
+        android:minWidth="132dp"
+        android:layout_toStartOf="@android:id/button1"
+        android:text="@string/quick_settings_more_settings"
+        android:textAppearance="@style/TextAppearance.QS.DetailButton" />
 
     <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_below="@android:id/custom" />
+        android:layout_above="@android:id/button1" />
 
 </RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_detail_header.xml b/packages/SystemUI/res/layout/qs_detail_header.xml
new file mode 100644
index 0000000..fcbb32c
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_detail_header.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/BrightnessDialogContainer"
+    android:background="@drawable/btn_borderless_rect" >
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+    <Switch
+        android:id="@android:id/toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:clickable="false"
+        android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_detail_item.xml b/packages/SystemUI/res/layout/qs_detail_item.xml
new file mode 100644
index 0000000..c5eaed9
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_detail_item.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/qs_detail_item_height"
+    android:gravity="center_vertical"
+    android:background="@drawable/btn_borderless_rect"
+    android:clickable="true"
+    android:orientation="horizontal" >
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:layout_marginEnd="12dp" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="20dp"
+        android:orientation="vertical" >
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="2dp"
+            android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index 97ed9a0..58547b9 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -30,7 +30,7 @@
     <com.android.systemui.settings.ToggleSlider
         android:id="@+id/brightness_slider"
         android:layout_width="0dp"
-        android:layout_height="44dp"
+        android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:layout_weight="1"
         systemui:text="@string/status_bar_settings_auto_brightness_label" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index d239208..70589b7 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -135,13 +135,22 @@
         android:textColor="#ffffff"
         android:singleLine="true" />
 
-    <include
-        layout="@layout/quick_settings_brightness_dialog"
-        android:id="@+id/brightness_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        />
+
+        <include
+            android:id="@+id/qs_detail_header"
+            layout="@layout/qs_detail_header"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            />
+        <include
+            android:id="@+id/brightness_container"
+            layout="@layout/quick_settings_brightness_dialog"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            />
+
 
     <TextView
         android:id="@+id/header_debug_info"
diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml
index 6d63bb0..6ab8cf3 100644
--- a/packages/SystemUI/res/layout/zen_mode_condition.xml
+++ b/packages/SystemUI/res/layout/zen_mode_condition.xml
@@ -41,7 +41,7 @@
 
     <ImageView
         android:id="@android:id/button1"
-        style="@style/BorderlessButton"
+        style="@style/QSBorderlessButton"
         android:layout_width="@dimen/zen_mode_condition_height"
         android:layout_height="@dimen/zen_mode_condition_height"
         android:layout_alignParentEnd="true"
@@ -53,7 +53,7 @@
 
     <ImageView
         android:id="@android:id/button2"
-        style="@style/BorderlessButton"
+        style="@style/QSBorderlessButton"
         android:layout_width="@dimen/zen_mode_condition_height"
         android:layout_height="@dimen/zen_mode_condition_height"
         android:layout_alignParentEnd="true"
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 0a8f852..0420cbc 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -42,7 +42,7 @@
 
     <TextView
         android:id="@android:id/button2"
-        style="@style/BorderlessButton"
+        style="@style/QSBorderlessButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_alignParentEnd="true"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bdbedad..a2757c3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -168,6 +168,9 @@
     <dimen name="qs_tile_padding_bottom">16dp</dimen>
     <dimen name="qs_tile_spacing">4dp</dimen>
     <dimen name="qs_panel_padding_bottom">8dp</dimen>
+    <dimen name="qs_detail_item_height">48dp</dimen>
+    <dimen name="qs_detail_item_height_connected">72dp</dimen>
+
 
     <!-- How far the expanded QS panel peeks from the header in collapsed state. -->
     <dimen name="qs_peek_height">8dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a8799f7..a5cfdbc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -534,6 +534,10 @@
     <string name="quick_settings_color_space_label">Color correction mode</string>
     <!-- QuickSettings: Control panel: Label for button that navigates to settings. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_more_settings">More settings</string>
+    <!-- QuickSettings: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_done">Done</string>
+    <!-- QuickSettings: Control panel: Label for connected device. [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_connected">Connected</string>
     <!-- QuickSettings: Tethering. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_tethering_label">Tethering</string>
     <!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e5d5b03..dd1b749 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -176,6 +176,7 @@
         <item name="android:textSize">14sp</item>
         <item name="android:textAllCaps">true</item>
         <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:gravity">center</item>
     </style>
 
     <style name="BaseBrightnessDialogContainer">
@@ -233,6 +234,11 @@
         <item name="android:colorControlActivated">#ffffffff</item>
     </style>
 
+     <style name="QSBorderlessButton">
+        <item name="android:padding">12dp</item>
+        <item name="android:background">@drawable/btn_borderless_rect</item>
+        <item name="android:gravity">center</item>
+    </style>
     <style name="BorderlessButton" parent="@android:style/Widget.Material.Button.Borderless" />
 
     <style name="BorderlessButton.Tiny">
diff --git a/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java b/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java
index ff904b3..327ed6a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java
@@ -21,23 +21,19 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.view.View;
+import android.view.ViewAnimationUtils;
 
 /** Helper for view-level circular clip animations. **/
 public class CircularClipper {
 
     private final View mTarget;
 
-    private Utils mUtils;
     private ValueAnimator mAnimator;
 
     public CircularClipper(View target) {
         mTarget = target;
     }
 
-    public void setUtils(Utils utils) {
-        mUtils = utils;
-    }
-
     public void animateCircularClip(int x, int y, boolean in, AnimatorListener listener) {
         if (mAnimator != null) {
             mAnimator.cancel();
@@ -49,14 +45,7 @@
         r = (int) Math.max(r, Math.ceil(Math.sqrt(w * w + h * h)));
         r = (int) Math.max(r, Math.ceil(Math.sqrt(x * x + h * h)));
 
-        if (mUtils == null) {
-                mTarget.setVisibility(in ? View.VISIBLE : View.GONE);
-            if (listener != null) {
-                listener.onAnimationEnd(null);
-            }
-            return;
-        }
-        mAnimator = mUtils.createRevealAnimator(mTarget, x, y, 0, r);
+        mAnimator = ViewAnimationUtils.createCircularReveal(mTarget, x, y, 0, r);
         mAnimator.removeAllListeners();
         if (listener != null) {
             mAnimator.addListener(listener);
@@ -83,9 +72,4 @@
             mTarget.setVisibility(View.GONE);
         };
     };
-
-    public interface Utils {
-        ValueAnimator createRevealAnimator(View v, int centerX,  int centerY,
-                float startRadius, float endRadius);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index d152887..51befd6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -20,15 +20,17 @@
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Message;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.FrameLayout;
 
 import com.android.systemui.R;
+import com.android.systemui.qs.QSTile.DetailAdapter;
 
 import java.util.ArrayList;
 
@@ -38,7 +40,10 @@
 
     private final Context mContext;
     private final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
-    private final FrameLayout mDetail;
+    private final View mDetail;
+    private final ViewGroup mDetailContent;
+    private final View mDetailSettingsButton;
+    private final View mDetailDoneButton;
     private final CircularClipper mClipper;
     private final H mHandler = new H();
 
@@ -63,8 +68,10 @@
         super(context, attrs);
         mContext = context;
 
-        mDetail = new FrameLayout(mContext);
-        mDetail.setBackgroundColor(mContext.getResources().getColor(R.color.system_primary_color));
+        mDetail = LayoutInflater.from(context).inflate(R.layout.qs_detail, this, false);
+        mDetailContent = (ViewGroup) mDetail.findViewById(android.R.id.content);
+        mDetailSettingsButton = mDetail.findViewById(android.R.id.button2);
+        mDetailDoneButton = mDetail.findViewById(android.R.id.button1);
         mDetail.setVisibility(GONE);
         mDetail.setClickable(true);
         addView(mDetail);
@@ -91,10 +98,6 @@
         }
     }
 
-    public void setUtils(CircularClipper.Utils utils) {
-        mClipper.setUtils(utils);
-    }
-
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
@@ -141,6 +144,12 @@
             public void onShowDetail(boolean show) {
                 QSPanel.this.showDetail(show, r);
             }
+            @Override
+            public void onToggleStateChanged(boolean state) {
+                if (mDetailRecord == r) {
+                    fireToggleStateChanged(state);
+                }
+            }
         });
         final View.OnClickListener click = new View.OnClickListener() {
             @Override
@@ -165,20 +174,34 @@
         if (r == null) return;
         AnimatorListener listener = null;
         if (show) {
-            if (mDetailRecord != null) return;
-            if (r.detailView == null) {
-                r.detailView = r.tile.createDetailView(mContext, mDetail);
-            }
-            if (r.detailView == null) return;
+            if (mDetailRecord != null) return;  // already showing something in detail
+            r.detailAdapter = r.tile.getDetailAdapter();
+            if (r.detailAdapter == null) return;
+            r.detailView = r.detailAdapter.createDetailView(mContext, r.detailView, mDetailContent);
+            if (r.detailView == null) throw new IllegalStateException("Must return detail view");
+            mDetailDoneButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    showDetail(false, mDetailRecord);
+                }
+            });
+            final Intent settingsIntent = r.detailAdapter.getSettingsIntent();
+            mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
+            mDetailSettingsButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mDetailRecord.tile.mHost.startSettingsActivity(settingsIntent);
+                }
+            });
             mDetailRecord = r;
-            mDetail.removeAllViews();
+            mDetailContent.removeAllViews();
             mDetail.bringToFront();
-            mDetail.addView(r.detailView);
+            mDetailContent.addView(r.detailView);
         } else {
             if (mDetailRecord == null) return;
             listener = mTeardownDetailWhenDone;
         }
-        fireShowingDetail(show);
+        fireShowingDetail(show ? r.detailAdapter : null);
         int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
         int y = r.tileView.getTop() + r.tileView.getHeight() / 2;
         mClipper.animateCircularClip(x, y, show, listener);
@@ -215,11 +238,7 @@
             record.tileView.measure(exactly(cw), exactly(ch));
         }
         int h = rows == 0 ? 0 : (getRowTop(rows) + mPanelPaddingBottom);
-        mDetail.measure(exactly(width), unspecified());
-        if (mDetail.getVisibility() == VISIBLE && mDetail.getChildCount() > 0) {
-            final int dmh = mDetail.getMeasuredHeight();
-            if (dmh > 0) h = Math.max(h, dmh);
-        }
+        mDetail.measure(exactly(width), exactly(h));
         setMeasuredDimension(width, h);
     }
 
@@ -227,10 +246,6 @@
         return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
     }
 
-    private static int unspecified() {
-        return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-    }
-
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         final int w = getWidth();
@@ -263,9 +278,15 @@
         return cols;
     }
 
-    private void fireShowingDetail(boolean showingDetail) {
+    private void fireShowingDetail(QSTile.DetailAdapter detail) {
         if (mCallback != null) {
-            mCallback.onShowingDetail(showingDetail);
+            mCallback.onShowingDetail(detail);
+        }
+    }
+
+    private void fireToggleStateChanged(boolean state) {
+        if (mCallback != null) {
+            mCallback.onToggleStateChanged(state);
         }
     }
 
@@ -286,18 +307,20 @@
         QSTile<?> tile;
         QSTileView tileView;
         View detailView;
+        DetailAdapter detailAdapter;
         int row;
         int col;
     }
 
     private final AnimatorListenerAdapter mTeardownDetailWhenDone = new AnimatorListenerAdapter() {
         public void onAnimationEnd(Animator animation) {
-            mDetail.removeAllViews();
+            mDetailContent.removeAllViews();
             mDetailRecord = null;
         };
     };
 
     public interface Callback {
-        void onShowingDetail(boolean showingDetail);
+        void onShowingDetail(QSTile.DetailAdapter detail);
+        void onToggleStateChanged(boolean state);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 5901dd5..62c9d9f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -83,10 +83,18 @@
         return new QSTileView(context);
     }
 
-    public View createDetailView(Context context, ViewGroup root) {
+    public DetailAdapter getDetailAdapter() {
         return null; // optional
     }
 
+    public interface DetailAdapter {
+        int getTitle();
+        Boolean getToggleState();
+        View createDetailView(Context context, View convertView, ViewGroup parent);
+        Intent getSettingsIntent();
+        void setToggleState(boolean state);
+    }
+
     // safe to call from any thread
 
     public void setCallback(Callback callback) {
@@ -117,6 +125,10 @@
         mHandler.obtainMessage(H.USER_SWITCH, newUserId).sendToTarget();
     }
 
+    public void fireToggleStateChanged(boolean state) {
+        mHandler.obtainMessage(H.TOGGLE_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
+    }
+
     // call only on tile worker looper
 
     private void handleSetCallback(Callback callback) {
@@ -148,6 +160,12 @@
         }
     }
 
+    private void handleToggleStateChanged(boolean state) {
+        if (mCallback != null) {
+            mCallback.onToggleStateChanged(state);
+        }
+    }
+
     protected void handleUserSwitch(int newUserId) {
         handleRefreshState(null);
     }
@@ -159,6 +177,7 @@
         private static final int REFRESH_STATE = 4;
         private static final int SHOW_DETAIL = 5;
         private static final int USER_SWITCH = 6;
+        private static final int TOGGLE_STATE_CHANGED = 7;
 
         private H(Looper looper) {
             super(looper);
@@ -186,6 +205,9 @@
                 } else if (msg.what == USER_SWITCH) {
                     name = "handleUserSwitch";
                     handleUserSwitch(msg.arg1);
+                } else if (msg.what == TOGGLE_STATE_CHANGED) {
+                    name = "handleToggleStateChanged";
+                    handleToggleStateChanged(msg.arg1 != 0);
                 }
             } catch (Throwable t) {
                 final String error = "Error in " + name;
@@ -198,6 +220,7 @@
     public interface Callback {
         void onStateChanged(State state);
         void onShowDetail(boolean show);
+        void onToggleStateChanged(boolean state);
     }
 
     public interface Host {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
index c4bdb19..3bdea79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
@@ -22,11 +22,9 @@
 import android.content.IntentFilter;
 import android.media.AudioManager;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewGroup;
-import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -47,45 +45,8 @@
     }
 
     @Override
-    public View createDetailView(Context context, ViewGroup root) {
-        final View v = LayoutInflater.from(context).inflate(R.layout.qs_detail, root, false);
-        final TextView title = (TextView) v.findViewById(android.R.id.title);
-        title.setText(R.string.quick_settings_notifications_label);
-        final View close = v.findViewById(android.R.id.button1);
-        close.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                showDetail(false);
-            }
-        });
-        final ViewGroup content = (ViewGroup) v.findViewById(android.R.id.content);
-        final VolumeComponent volumeComponent = mHost.getVolumeComponent();
-        final VolumePanel vp = new VolumePanel(mContext, content, mZenController);
-        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;
+    public DetailAdapter getDetailAdapter() {
+        return mDetailAdapter;
     }
 
     @Override
@@ -164,4 +125,58 @@
             }
         }
     };
+
+    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 a236497..84eee24 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -21,23 +21,34 @@
 import android.content.res.Resources;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.QSTileView;
 import com.android.systemui.qs.SignalTileView;
 import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPoint;
 import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
 
 /** Quick settings tile: Wifi **/
 public class WifiTile extends QSTile<QSTile.SignalState> {
     private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
+    private static final int MAX_ITEMS = 4; // TODO temporary visual restriction
 
     private final NetworkController mController;
+    private final WifiDetailAdapter mDetailAdapter;
 
     public WifiTile(Host host) {
         super(host);
         mController = host.getNetworkController();
+        mDetailAdapter = new WifiDetailAdapter();
     }
 
     @Override
@@ -54,12 +65,20 @@
     public void setListening(boolean listening) {
         if (listening) {
             mController.addNetworkSignalChangedCallback(mCallback);
+            mController.addAccessPointCallback(mDetailAdapter);
+            mController.scanForAccessPoints();
         } else {
             mController.removeNetworkSignalChangedCallback(mCallback);
+            mController.removeAccessPointCallback(mDetailAdapter);
         }
     }
 
     @Override
+    public DetailAdapter getDetailAdapter() {
+        return mDetailAdapter;
+    }
+
+    @Override
     public QSTileView createTileView(Context context) {
         return new SignalTileView(context);
     }
@@ -71,7 +90,7 @@
 
     @Override
     protected void handleSecondaryClick() {
-        mHost.startSettingsActivity(WIFI_SETTINGS);
+        showDetail(true);
     }
 
     @Override
@@ -83,6 +102,11 @@
 
         boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
         boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null);
+        boolean enabledChanging = state.enabled != cb.enabled;
+        if (enabledChanging) {
+            mDetailAdapter.postUpdateItems();
+            fireToggleStateChanged(cb.enabled);
+        }
         state.enabled = cb.enabled;
         state.connected = wifiConnected;
         state.activityIn = cb.enabled && cb.activityIn;
@@ -169,4 +193,96 @@
             // noop
         }
     };
+
+    private final class WifiDetailAdapter implements DetailAdapter,
+            NetworkController.AccessPointCallback {
+
+        private LinearLayout mItems;
+        private AccessPoint[] mAccessPoints;
+
+        @Override
+        public int getTitle() {
+            return R.string.quick_settings_wifi_label;
+        }
+
+        public Intent getSettingsIntent() {
+            return WIFI_SETTINGS;
+        }
+
+        @Override
+        public Boolean getToggleState() {
+            return mState.enabled;
+        }
+
+        @Override
+        public void setToggleState(boolean state) {
+            if (DEBUG) Log.d(TAG, "setToggleState " + state);
+            mController.setWifiEnabled(state);
+            showDetail(false);
+        }
+
+        @Override
+        public View createDetailView(Context context, View convertView, ViewGroup parent) {
+            if (convertView != null) return convertView;
+            mItems = new LinearLayout(context);
+            mItems.setOrientation(LinearLayout.VERTICAL);
+            updateItems();
+            return mItems;
+        }
+
+        @Override
+        public void onAccessPointsChanged(final AccessPoint[] accessPoints) {
+            mUiHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mAccessPoints = accessPoints;
+                    updateItems();
+                }
+            });
+        }
+
+        public void postUpdateItems() {
+            mUiHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    updateItems();
+                }
+            });
+        }
+
+        private void updateItems() {
+            if (mItems == null) return;
+            mItems.removeAllViews();
+            if (mAccessPoints == null || mAccessPoints.length == 0 || !mState.enabled) return;
+            for (int i = 0; i < mAccessPoints.length; i++) {
+                final AccessPoint ap = mAccessPoints[i];
+                if (ap == null) continue;
+                final View item = LayoutInflater.from(mContext).inflate(R.layout.qs_detail_item,
+                        mItems, false);
+                final ImageView iv = (ImageView) item.findViewById(android.R.id.icon);
+                iv.setImageResource(ap.iconId);
+                final TextView title = (TextView) item.findViewById(android.R.id.title);
+                title.setText(ap.ssid);
+                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));
+                    summary.setText(R.string.quick_settings_connected);
+                } else {
+                    summary.setVisibility(View.GONE);
+                }
+                item.setOnClickListener(new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        if (!ap.isConnected) {
+                            mController.connect(ap);
+                        }
+                        showDetail(false);
+                    }
+                });
+                mItems.addView(item);
+                if (mItems.getChildCount() == MAX_ITEMS) break;
+            }
+        }
+    };
 }
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 3fd3175..b0a7d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -32,7 +32,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
@@ -78,7 +77,6 @@
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
-import android.view.ViewAnimationUtils;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewPropertyAnimator;
@@ -101,7 +99,6 @@
 import com.android.systemui.R;
 import com.android.systemui.doze.DozeService;
 import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.qs.CircularClipper;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.ActivatableNotificationView;
@@ -717,14 +714,6 @@
         // Set up the quick settings tile panel
         mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
         if (mQSPanel != null) {
-            mQSPanel.setUtils(new CircularClipper.Utils() {
-                @Override
-                public ValueAnimator createRevealAnimator(View v, int centerX, int centerY,
-                        float startRadius, float endRadius) {
-                    return ViewAnimationUtils.createCircularReveal(v, centerX, centerY,
-                            startRadius, endRadius);
-                }
-            });
             final QSTileHost qsh = new QSTileHost(mContext, this,
                     mBluetoothController, mLocationController, mRotationLockController,
                     mNetworkController, mZenModeController, null /*tethering*/,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 3e2dcef..79ae60c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Outline;
@@ -26,10 +28,12 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
+import android.widget.Switch;
 import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.qs.QSPanel;
+import com.android.systemui.qs.QSTile;
 import com.android.systemui.settings.BrightnessController;
 import com.android.systemui.settings.ToggleSlider;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -60,6 +64,7 @@
     private View mSignalCluster;
     private View mSettingsButton;
     private View mBrightnessContainer;
+    private View mQsDetailHeader;
     private View mEmergencyCallsOnly;
     private TextView mChargingInfo;
 
@@ -104,6 +109,8 @@
         mBrightnessController = new BrightnessController(getContext(),
                 (ImageView) findViewById(R.id.brightness_icon),
                 (ToggleSlider) findViewById(R.id.brightness_slider));
+        mQsDetailHeader = findViewById(R.id.qs_detail_header);
+        mQsDetailHeader.setAlpha(0);
         mEmergencyCallsOnly = findViewById(R.id.header_emergency_calls_only);
         mChargingInfo = (TextView) findViewById(R.id.header_charging_info);
         loadDimens();
@@ -219,6 +226,7 @@
         mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
         mSettingsButton.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
         mBrightnessContainer.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+        mQsDetailHeader.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
         if (mStatusIcons != null) {
             mStatusIcons.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
         }
@@ -375,13 +383,6 @@
         }
     }
 
-    private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
-        @Override
-        public void onShowingDetail(boolean showingDetail) {
-            mBrightnessContainer.animate().alpha(showingDetail ? 0 : 1).withLayer().start();
-        }
-    };
-
     @Override
     public boolean shouldDelayChildPressedState() {
         return true;
@@ -418,4 +419,69 @@
     public boolean hasOverlappingRendering() {
         return !mKeyguardShowing || mExpanded;
     }
+
+    private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
+        @Override
+        public void onToggleStateChanged(final boolean state) {
+            post(new Runnable() {
+                @Override
+                public void run() {
+                    handleToggleStateChanged(state);
+                }
+            });
+        }
+
+        @Override
+        public void onShowingDetail(final QSTile.DetailAdapter detail) {
+            post(new Runnable() {
+                @Override
+                public void run() {
+                    handleShowingDetail(detail);
+                }
+            });
+        }
+
+        private void handleToggleStateChanged(boolean state) {
+            final Switch headerSwitch = (Switch)
+                    mQsDetailHeader.findViewById(android.R.id.toggle);
+            headerSwitch.setChecked(state);
+        }
+
+        private void handleShowingDetail(final QSTile.DetailAdapter detail) {
+            final boolean showingDetail = detail != null;
+            transition(mBrightnessContainer, !showingDetail);
+            transition(mQsDetailHeader, showingDetail);
+            if (showingDetail) {
+                final TextView headerTitle = (TextView)
+                        mQsDetailHeader.findViewById(android.R.id.title);
+                headerTitle.setText(detail.getTitle());
+                final Switch headerSwitch = (Switch)
+                        mQsDetailHeader.findViewById(android.R.id.toggle);
+                final Boolean toggleState = detail.getToggleState();
+                if (toggleState == null) {
+                    headerSwitch.setVisibility(INVISIBLE);
+                    mQsDetailHeader.setClickable(false);
+                } else {
+                    headerSwitch.setVisibility(VISIBLE);
+                    headerSwitch.setChecked(toggleState);
+                    mQsDetailHeader.setClickable(true);
+                    mQsDetailHeader.setOnClickListener(new OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            detail.setToggleState(!toggleState);
+                        }
+                    });
+                }
+            } else {
+                mQsDetailHeader.setClickable(false);
+            }
+        }
+
+        private void transition(final View v, final boolean in) {
+            if (in) {
+                v.bringToFront();
+            }
+            v.animate().alpha(in ? 1 : 0).withLayer().start();
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 1f68860..7e113699 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -33,4 +33,23 @@
                 String dataTypeContentDescriptionId, String description, boolean noSim);
         void onAirplaneModeChanged(boolean enabled);
     }
+
+    void addAccessPointCallback(AccessPointCallback callback);
+    void removeAccessPointCallback(AccessPointCallback callback);
+    void scanForAccessPoints();
+    void connect(AccessPoint ap);
+
+    public interface AccessPointCallback {
+        void onAccessPointsChanged(AccessPoint[] accessPoints);
+    }
+
+    public static class AccessPoint {
+        public static final int NO_NETWORK = -1;  // see WifiManager
+
+        public int networkId;
+        public int iconId;
+        public String ssid;
+        public boolean isConnected;
+        public int level;  // 0 - 5
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 4e54e41..4b94ebd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -169,6 +169,8 @@
         void setIsAirplaneMode(boolean is, int airplaneIcon);
     }
 
+    private final WifiAccessPointController mAccessPoints;
+
     /**
      * Construct this controller object and register for updates.
      */
@@ -237,6 +239,7 @@
         updateAirplaneMode();
 
         mLastLocale = mContext.getResources().getConfiguration().locale;
+        mAccessPoints = new WifiAccessPointController(mContext);
     }
 
     public boolean hasMobileDataFeature() {
@@ -282,6 +285,26 @@
     }
 
     @Override
+    public void addAccessPointCallback(AccessPointCallback callback) {
+        mAccessPoints.addCallback(callback);
+    }
+
+    @Override
+    public void removeAccessPointCallback(AccessPointCallback callback) {
+        mAccessPoints.removeCallback(callback);
+    }
+
+    @Override
+    public void scanForAccessPoints() {
+        mAccessPoints.scan();
+    }
+
+    @Override
+    public void connect(AccessPoint ap) {
+        mAccessPoints.connect(ap);
+    }
+
+    @Override
     public void setWifiEnabled(final boolean enabled) {
         new AsyncTask<Void, Void, Void>() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiAccessPointController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiAccessPointController.java
new file mode 100644
index 0000000..09e7472
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiAccessPointController.java
@@ -0,0 +1,200 @@
+/*
+ * 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.statusbar.policy;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.ActionListener;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPoint;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointCallback;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class WifiAccessPointController {
+    private static final String TAG = "WifiAccessPointController";
+    private static final boolean DEBUG = false;
+
+    private static final int[] ICONS = {
+        R.drawable.ic_qs_wifi_0,
+        R.drawable.ic_qs_wifi_full_1,
+        R.drawable.ic_qs_wifi_full_2,
+        R.drawable.ic_qs_wifi_full_3,
+        R.drawable.ic_qs_wifi_full_4,
+    };
+
+    private final Context mContext;
+    private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>();
+    private final WifiManager mWifiManager;
+    private final Receiver mReceiver = new Receiver();
+
+    private boolean mScanning;
+
+    public WifiAccessPointController(Context context) {
+        mContext = context;
+        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+    }
+
+    public void addCallback(AccessPointCallback callback) {
+        if (callback == null || mCallbacks.contains(callback)) return;
+        if (DEBUG) Log.d(TAG, "addCallback " + callback);
+        mCallbacks.add(callback);
+        mReceiver.setListening(!mCallbacks.isEmpty());
+    }
+
+    public void removeCallback(AccessPointCallback callback) {
+        if (callback == null) return;
+        if (DEBUG) Log.d(TAG, "removeCallback " + callback);
+        mCallbacks.remove(callback);
+        mReceiver.setListening(!mCallbacks.isEmpty());
+    }
+
+    public void scan() {
+        if (mScanning) return;
+        if (DEBUG) Log.d(TAG, "scan!");
+        mScanning = mWifiManager.startScan();
+    }
+
+    public void connect(AccessPoint ap) {
+        if (ap == null || ap.networkId < 0) return;
+        if (DEBUG) Log.d(TAG, "connect networkId=" + ap.networkId);
+        mWifiManager.connect(ap.networkId, new ActionListener() {
+            @Override
+            public void onSuccess() {
+                if (DEBUG) Log.d(TAG, "connect success");
+            }
+
+            @Override
+            public void onFailure(int reason) {
+                if (DEBUG) Log.d(TAG, "connect failure reason=" + reason);
+            }
+        });
+    }
+
+    private void fireCallback(AccessPoint[] aps) {
+        for (AccessPointCallback callback : mCallbacks) {
+            callback.onAccessPointsChanged(aps);
+        }
+    }
+
+    private static String trimDoubleQuotes(String v) {
+        return v != null && v.length() >= 2 && v.charAt(0) == '\"'
+                && v.charAt(v.length() - 1) == '\"' ? v.substring(1, v.length() - 1) : v;
+    }
+
+    private int getConnectedNetworkId() {
+        final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+        return wifiInfo != null ? wifiInfo.getNetworkId() : AccessPoint.NO_NETWORK;
+    }
+
+    private ArrayMap<String, WifiConfiguration> getConfiguredNetworksBySsid() {
+        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+        if (configs == null || configs.size() == 0) return ArrayMap.EMPTY;
+        final ArrayMap<String, WifiConfiguration> rt = new ArrayMap<String, WifiConfiguration>();
+        for (WifiConfiguration config : configs) {
+            rt.put(trimDoubleQuotes(config.SSID), config);
+        }
+        return rt;
+    }
+
+    private void updateAccessPoints() {
+        final int connectedNetworkId = getConnectedNetworkId();
+        if (DEBUG) Log.d(TAG, "connectedNetworkId: " + connectedNetworkId);
+        final List<ScanResult> scanResults = mWifiManager.getScanResults();
+        final ArrayMap<String, WifiConfiguration> configured = getConfiguredNetworksBySsid();
+        if (DEBUG) Log.d(TAG, "scanResults: " + scanResults);
+        final List<AccessPoint> aps = new ArrayList<AccessPoint>(scanResults.size());
+        final ArraySet<String> ssids = new ArraySet<String>();
+        for (ScanResult scanResult : scanResults) {
+            final String ssid = scanResult.SSID;
+            if (TextUtils.isEmpty(ssid) || ssids.contains(ssid)) continue;
+            if (!configured.containsKey(ssid)) continue;
+            ssids.add(ssid);
+            final WifiConfiguration config = configured.get(ssid);
+            final int level = WifiManager.calculateSignalLevel(scanResult.level, ICONS.length);
+            final AccessPoint ap = new AccessPoint();
+            ap.networkId = config != null ? config.networkId : AccessPoint.NO_NETWORK;
+            ap.ssid = ssid;
+            ap.iconId = ICONS[level];
+            ap.isConnected = ap.networkId != AccessPoint.NO_NETWORK
+                    && ap.networkId == connectedNetworkId;
+            ap.level = level;
+            aps.add(ap);
+        }
+        Collections.sort(aps, mByStrength);
+        fireCallback(aps.toArray(new AccessPoint[aps.size()]));
+    }
+
+    private final Comparator<AccessPoint> mByStrength = new Comparator<AccessPoint> () {
+        @Override
+        public int compare(AccessPoint lhs, AccessPoint rhs) {
+            return -Integer.compare(score(lhs), score(rhs));
+        }
+
+        private int score(AccessPoint ap) {
+            return ap.level + (ap.isConnected ? 10 : 0);
+        }
+    };
+
+    private final class Receiver extends BroadcastReceiver {
+        private boolean mRegistered;
+
+        public void setListening(boolean listening) {
+            if (listening && !mRegistered) {
+                if (DEBUG) Log.d(TAG, "Registering receiver");
+                final IntentFilter filter = new IntentFilter();
+                filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+                filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+                filter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
+                filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+                filter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+                filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
+                filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+                filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+                mContext.registerReceiver(this, filter);
+                mRegistered = true;
+            } else if (!listening && mRegistered) {
+                if (DEBUG) Log.d(TAG, "Unregistering receiver");
+                mContext.unregisterReceiver(this);
+                mRegistered = false;
+            }
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (DEBUG) Log.d(TAG, "onReceive " + intent.getAction());
+            if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) {
+                updateAccessPoints();
+                mScanning = false;
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 99cba4d..53daaae 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -358,7 +358,7 @@
             // embedded mode
             mDialog = null;
             mView = LayoutInflater.from(mContext).inflate(
-                    com.android.systemui.R.layout.volume_panel, parent, true);
+                    com.android.systemui.R.layout.volume_panel, parent, false);
         }
         mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
         mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
@@ -384,6 +384,10 @@
         listenToRingerMode();
     }
 
+    public View getContentView() {
+        return mView;
+    }
+
     private void setLayoutDirection(int layoutDirection) {
         mPanel.setLayoutDirection(layoutDirection);
         updateStates();
@@ -628,7 +632,8 @@
         if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
         if (mZenPanel == null) {
             mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
-            mZenPanel.init(mZenController, mDialog != null ? 'D' : 'E');
+            final boolean isDialog = mDialog != null;
+            mZenPanel.init(mZenController, isDialog ? 'D' : 'E', isDialog);
             mZenPanel.setCallback(new ZenModePanel.Callback() {
                 @Override
                 public void onMoreSettings() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 798e7fa..9917944 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -67,6 +67,7 @@
     private char mLogTag = '?';
     private String mTag;
     private LinearLayout mConditions;
+    private View mMoreSettings;
     private Callback mCallback;
     private ZenModeController mController;
     private boolean mRequestingConditions;
@@ -91,7 +92,8 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mConditions = (LinearLayout) findViewById(android.R.id.content);
-        findViewById(android.R.id.button2).setOnClickListener(new View.OnClickListener() {
+        mMoreSettings = findViewById(android.R.id.button2);
+        mMoreSettings.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 fireMoreSettings();
@@ -154,12 +156,13 @@
         }
     }
 
-    public void init(ZenModeController controller, char logTag) {
+    public void init(ZenModeController controller, char logTag, boolean moreSettings) {
         mController = controller;
         mLogTag = logTag;
         updateTag();
         mExitConditionId = mController.getExitConditionId();
         if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId);
+        mMoreSettings.setVisibility(moreSettings ? VISIBLE : GONE);
         mConditions.removeAllViews();
         mController.addCallback(mZenCallback);
         if (mShowing) {