Merge "Work on QS landscape fitting" into nyc-dev
diff --git a/packages/SystemUI/res/layout/battery_detail.xml b/packages/SystemUI/res/layout/battery_detail.xml
index af3e379..8e7feec94 100644
--- a/packages/SystemUI/res/layout/battery_detail.xml
+++ b/packages/SystemUI/res/layout/battery_detail.xml
@@ -26,10 +26,13 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingStart="72dp"
-        android:paddingBottom="@dimen/battery_detail_graph_space_top"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textColor="?android:attr/colorAccent" />
 
+    <com.android.systemui.ResizingSpace
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/battery_detail_graph_space_top" />
+
     <com.android.settingslib.graph.UsageView
         android:id="@+id/battery_usage"
         android:layout_width="match_parent"
@@ -40,11 +43,14 @@
         android:colorAccent="?android:attr/colorAccent"
         systemui:textColor="#66FFFFFF" />
 
+    <com.android.systemui.ResizingSpace
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/battery_detail_graph_space_bottom" />
+
     <View
         android:layout_width="match_parent"
         android:layout_height="1dp"
         android:background="?android:attr/listDivider"
-        android:layout_marginTop="@dimen/battery_detail_graph_space_bottom"
         android:layout_marginBottom="8dp" />
 
     <RelativeLayout
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 3358a18..af2a285 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -25,13 +25,15 @@
     android:visibility="invisible"
     android:orientation="vertical">
 
+    <com.android.systemui.ResizingSpace
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_detail_margin_top" />
+
     <include
         android:id="@+id/qs_detail_header"
         layout="@layout/qs_detail_header"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="28dp"
-        />
+        android:layout_height="wrap_content" />
 
     <com.android.systemui.statusbar.AlphaOptimizedImageView
         android:id="@+id/qs_detail_header_progress"
diff --git a/packages/SystemUI/res/layout/qs_detail_items.xml b/packages/SystemUI/res/layout/qs_detail_items.xml
index c22e42c..f1a8d63 100644
--- a/packages/SystemUI/res/layout/qs_detail_items.xml
+++ b/packages/SystemUI/res/layout/qs_detail_items.xml
@@ -16,17 +16,19 @@
 -->
 <!-- extends FrameLayout -->
 <com.android.systemui.qs.QSDetailItems xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:sysui="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:paddingTop="16dp"
+    android:paddingTop="@dimen/qs_detail_items_padding_top"
     android:paddingStart="16dp"
     android:paddingEnd="16dp">
 
-    <LinearLayout
+    <com.android.systemui.qs.AutoSizingList
         android:id="@android:id/list"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical" />
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        sysui:itemHeight="@dimen/qs_detail_item_height" />
 
     <LinearLayout
         android:id="@android:id/empty"
@@ -48,9 +50,4 @@
             android:layout_marginTop="20dp"
             android:textAppearance="@style/TextAppearance.QS.DetailEmpty" />
     </LinearLayout>
-
-    <View
-        android:id="@+id/min_height_spacer"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"/>
-</com.android.systemui.qs.QSDetailItems>
\ No newline at end of file
+</com.android.systemui.qs.QSDetailItems>
diff --git a/packages/SystemUI/res/values-h560dp/config.xml b/packages/SystemUI/res/values-h560dp/config.xml
deleted file mode 100644
index 8b576b9..0000000
--- a/packages/SystemUI/res/values-h560dp/config.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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
-  -->
-
-<resources>
-    <!-- The maximum number of items to be displayed in quick settings -->
-    <integer name="quick_settings_detail_max_item_count">6</integer>
-</resources>
-
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 26a81c8..c40797c 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -24,4 +24,14 @@
 
     <dimen name="docked_divider_handle_width">2dp</dimen>
     <dimen name="docked_divider_handle_height">16dp</dimen>
+
+    <dimen name="qs_tile_margin_top">2dp</dimen>
+    <dimen name="qs_brightness_padding_top">0dp</dimen>
+
+    <dimen name="battery_detail_graph_space_top">9dp</dimen>
+    <dimen name="battery_detail_graph_space_bottom">9dp</dimen>
+
+    <integer name="quick_settings_num_columns">4</integer>
+    <bool name="quick_settings_wide">true</bool>
+    <dimen name="qs_detail_margin_top">0dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw410dp/config.xml b/packages/SystemUI/res/values-sw410dp/config.xml
new file mode 100644
index 0000000..08b2f88
--- /dev/null
+++ b/packages/SystemUI/res/values-sw410dp/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <integer name="quick_settings_num_rows">2</integer>
+</resources>
diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml
new file mode 100644
index 0000000..5ce6524
--- /dev/null
+++ b/packages/SystemUI/res/values-sw410dp/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <dimen name="qs_detail_items_padding_top">16dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-sw540dp/config.xml b/packages/SystemUI/res/values-sw540dp/config.xml
new file mode 100644
index 0000000..e554fc6d
--- /dev/null
+++ b/packages/SystemUI/res/values-sw540dp/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <integer name="quick_settings_num_rows">3</integer>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 4ed15d5..49a7a29 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -40,4 +40,8 @@
 
     <dimen name="battery_detail_graph_space_top">27dp</dimen>
     <dimen name="battery_detail_graph_space_bottom">27dp</dimen>
+
+    <dimen name="qs_tile_margin_top">16dp</dimen>
+    <dimen name="qs_brightness_padding_top">6dp</dimen>
+    <dimen name="qs_detail_margin_top">28dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-w550dp-land/dimens.xml b/packages/SystemUI/res/values-w550dp-land/dimens.xml
index cd17bed..4160c83 100644
--- a/packages/SystemUI/res/values-w550dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-w550dp-land/dimens.xml
@@ -20,7 +20,4 @@
     <dimen name="notification_panel_width">544dp</dimen>
 
     <dimen name="qs_expand_margin">32dp</dimen>
-
-    <dimen name="battery_detail_graph_space_top">9dp</dimen>
-    <dimen name="battery_detail_graph_space_bottom">9dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 1e979fd..6dd8c52 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -97,5 +97,9 @@
     <declare-styleable name="DensityContainer">
         <attr name="android:layout" />
     </declare-styleable>
+
+    <declare-styleable name="AutoSizingList">
+        <attr name="itemHeight" format="dimension" />
+    </declare-styleable>
 </resources>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 6ce2a5d..42798a4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -92,11 +92,8 @@
     <!-- The number of columns in the QuickSettings -->
     <integer name="quick_settings_num_columns">3</integer>
 
-    <!-- The maximum number of rows in the QuickSettings -->
-    <integer name="quick_settings_max_rows">4</integer>
-
-    <!-- The maximum number of rows in the QuickSettings when on the keyguard -->
-    <integer name="quick_settings_max_rows_keyguard">3</integer>
+    <!-- The number of rows in the QuickSettings -->
+    <integer name="quick_settings_num_rows">1</integer>
 
     <!-- The number of columns that the top level tiles span in the QuickSettings -->
     <integer name="quick_settings_user_time_settings_tile_span">1</integer>
@@ -116,9 +113,6 @@
     <integer name="quick_settings_brightness_dialog_short_timeout">2000</integer>
     <integer name="quick_settings_brightness_dialog_long_timeout">4000</integer>
 
-    <!-- The maximum number of items to be displayed in quick settings -->
-    <integer name="quick_settings_detail_max_item_count">5</integer>
-
     <!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
     <bool name="config_show4GForLTE">true</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 75e57b0..a4eadbf 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -172,6 +172,7 @@
 
     <dimen name="qs_tile_height">88dp</dimen>
     <dimen name="qs_tile_margin">16dp</dimen>
+    <dimen name="qs_tile_margin_top">16dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
     <dimen name="qs_date_anim_translation">32dp</dimen>
@@ -201,10 +202,12 @@
     <dimen name="qs_detail_item_primary_text_size">16sp</dimen>
     <dimen name="qs_detail_item_secondary_text_size">14sp</dimen>
     <dimen name="qs_detail_empty_text_size">14sp</dimen>
+    <dimen name="qs_detail_margin_top">28dp</dimen>
     <dimen name="qs_data_usage_text_size">14sp</dimen>
     <dimen name="qs_data_usage_usage_text_size">36sp</dimen>
     <dimen name="qs_expand_margin">0dp</dimen>
     <dimen name="qs_battery_padding">2dp</dimen>
+    <dimen name="qs_detail_items_padding_top">4dp</dimen>
 
     <dimen name="segmented_button_spacing">0dp</dimen>
     <dimen name="borderless_button_radius">2dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/ResizingSpace.java b/packages/SystemUI/src/com/android/systemui/ResizingSpace.java
new file mode 100644
index 0000000..c2bc53e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ResizingSpace.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+public class ResizingSpace extends View {
+
+    private final int mWidth;
+    private final int mHeight;
+
+    public ResizingSpace(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        if (getVisibility() == VISIBLE) {
+            setVisibility(INVISIBLE);
+        }
+        TypedArray a = context.obtainStyledAttributes(attrs, android.R.styleable.ViewGroup_Layout);
+        mWidth = a.getResourceId(android.R.styleable.ViewGroup_Layout_layout_width, 0);
+        mHeight = a.getResourceId(android.R.styleable.ViewGroup_Layout_layout_height, 0);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        LayoutParams params = getLayoutParams();
+        boolean changed = false;
+        if (mWidth > 0) {
+            int width = getContext().getResources().getDimensionPixelOffset(mWidth);
+            if (width != params.width) {
+                params.width = width;
+                changed = true;
+            }
+        }
+        if (mHeight > 0) {
+            int height = getContext().getResources().getDimensionPixelOffset(mHeight);
+            if (height != params.height) {
+                params.height = height;
+                changed = true;
+            }
+        }
+        if (changed) {
+            setLayoutParams(params);
+        }
+    }
+
+    /**
+     * Draw nothing.
+     *
+     * @param canvas an unused parameter.
+     */
+    @Override
+    public void draw(Canvas canvas) {
+    }
+
+    /**
+     * Compare to: {@link View#getDefaultSize(int, int)}
+     * If mode is AT_MOST, return the child size instead of the parent size
+     * (unless it is too big).
+     */
+    private static int getDefaultSize2(int size, int measureSpec) {
+        int result = size;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+
+        switch (specMode) {
+            case MeasureSpec.UNSPECIFIED:
+                result = size;
+                break;
+            case MeasureSpec.AT_MOST:
+                result = Math.min(size, specSize);
+                break;
+            case MeasureSpec.EXACTLY:
+                result = specSize;
+                break;
+        }
+        return result;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(
+                getDefaultSize2(getSuggestedMinimumWidth(), widthMeasureSpec),
+                getDefaultSize2(getSuggestedMinimumHeight(), heightMeasureSpec));
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
new file mode 100644
index 0000000..00e6221
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import com.android.systemui.R;
+
+/**
+ * Similar to a ListView, but it will show only as many items as fit on screen and
+ * bind those instead of scrolling.
+ */
+public class AutoSizingList extends LinearLayout {
+
+    private static final String TAG = "AutoSizingList";
+    private final int mItemSize;
+    private final Handler mHandler;
+
+    private ListAdapter mAdapter;
+    private int mCount;
+
+    public AutoSizingList(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+
+        mHandler = new Handler();
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoSizingList);
+        mItemSize = a.getDimensionPixelSize(R.styleable.AutoSizingList_itemHeight, 0);
+    }
+
+    public void setAdapter(ListAdapter adapter) {
+        if (mAdapter != null) {
+            mAdapter.unregisterDataSetObserver(mDataObserver);
+        }
+        mAdapter = adapter;
+        if (adapter != null) {
+            adapter.registerDataSetObserver(mDataObserver);
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int requestedHeight = MeasureSpec.getSize(heightMeasureSpec);
+        if (requestedHeight != 0) {
+            int count = Math.min(requestedHeight / mItemSize, getDesiredCount());
+            if (mCount != count) {
+                postRebindChildren();
+                mCount = count;
+            }
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    private int getDesiredCount() {
+        return mAdapter != null ? mAdapter.getCount() : 0;
+    }
+
+    private void postRebindChildren() {
+        mHandler.post(mBindChildren);
+    }
+
+    private void rebindChildren() {
+        if (mAdapter == null) {
+            return;
+        }
+        for (int i = 0; i < mCount; i++) {
+            View v = i < getChildCount() ? getChildAt(i) : null;
+            View newView = mAdapter.getView(i, v, this);
+            if (newView != v) {
+                if (v != null) {
+                    removeView(v);
+                }
+                addView(newView, i);
+            }
+        }
+        // Ditch extra views.
+        while (getChildCount() > mCount) {
+            removeViewAt(getChildCount() - 1);
+        }
+    }
+
+    private final Runnable mBindChildren = new Runnable() {
+        @Override
+        public void run() {
+            rebindChildren();
+        }
+    };
+
+    private final DataSetObserver mDataObserver = new DataSetObserver() {
+        @Override
+        public void onChanged() {
+            if (mCount > getDesiredCount()) {
+                mCount = getDesiredCount();
+            }
+            postRebindChildren();
+        }
+
+        @Override
+        public void onInvalidated() {
+            postRebindChildren();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 115c9d0..5a23610 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -1,6 +1,8 @@
 package com.android.systemui.qs;
 
 import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.support.v4.view.PagerAdapter;
 import android.support.v4.view.ViewPager;
 import android.util.AttributeSet;
@@ -199,11 +201,22 @@
 
         @Override
         public boolean updateResources() {
-            if (super.updateResources()) {
-                mMaxRows = mColumns != 3 ? 2 : 3;
-                return true;
+            final int rows = getRows();
+            boolean changed = rows != mMaxRows;
+            if (changed) {
+                mMaxRows = rows;
+                requestLayout();
             }
-            return false;
+            return super.updateResources() || changed;
+        }
+
+        private int getRows() {
+            final Resources res = getContext().getResources();
+            if (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+                // Always have 3 rows in portrait.
+                return 3;
+            }
+            return Math.max(1, res.getInteger(R.integer.quick_settings_num_rows));
         }
 
         public void setMaxRows(int maxRows) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index 5d06aeb..0af5fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -100,7 +100,13 @@
         // Since we control our own bottom, be whatever size we want.
         // Otherwise the QSPanel ends up with 0 height when the window is only the
         // size of the status bar.
-        super.onMeasure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
+        mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(
+                MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED));
+        int width = mQSPanel.getMeasuredWidth();
+        int height = ((LayoutParams) mQSPanel.getLayoutParams()).topMargin
+                + mQSPanel.getMeasuredHeight();
+        super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
 
         // QSCustomizer is always be the height of the screen, but do this after
         // other measuring to avoid changing the height of the QSContainer.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
index 25b9105..2dd4a0a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
@@ -28,11 +28,10 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.BaseAdapter;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.TextView;
-
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 
@@ -45,16 +44,17 @@
 
     private final Context mContext;
     private final H mHandler = new H();
+    private final Adapter mAdapter = new Adapter();
 
     private String mTag;
     private Callback mCallback;
     private boolean mItemsVisible = true;
-    private LinearLayout mItems;
+    private AutoSizingList mItemList;
     private View mEmpty;
-    private View mMinHeightSpacer;
     private TextView mEmptyText;
     private ImageView mEmptyIcon;
-    private int mMaxItems;
+
+    private Item[] mItems;
 
     public QSDetailItems(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -73,27 +73,22 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mItems = (LinearLayout) findViewById(android.R.id.list);
-        mItems.setVisibility(GONE);
+        mItemList = (AutoSizingList) findViewById(android.R.id.list);
+        mItemList.setVisibility(GONE);
+        mItemList.setAdapter(mAdapter);
         mEmpty = findViewById(android.R.id.empty);
         mEmpty.setVisibility(GONE);
         mEmptyText = (TextView) mEmpty.findViewById(android.R.id.title);
         mEmptyIcon = (ImageView) mEmpty.findViewById(android.R.id.icon);
-        mMinHeightSpacer = findViewById(R.id.min_height_spacer);
-
-        // By default, a detail item view has fixed size.
-        mMaxItems = getResources().getInteger(
-                R.integer.quick_settings_detail_max_item_count);
-        setMinHeightInItems(mMaxItems);
     }
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         FontSizeUtils.updateFontSize(mEmptyText, R.dimen.qs_detail_empty_text_size);
-        int count = mItems.getChildCount();
+        int count = mItemList.getChildCount();
         for (int i = 0; i < count; i++) {
-            View item = mItems.getChildAt(i);
+            View item = mItemList.getChildAt(i);
             FontSizeUtils.updateFontSize(item, android.R.id.title,
                     R.dimen.qs_detail_item_primary_text_size);
             FontSizeUtils.updateFontSize(item, android.R.id.summary,
@@ -110,16 +105,6 @@
         mEmptyText.setText(text);
     }
 
-    /**
-     * Set the minimum height of this detail view, in item count.
-     */
-    public void setMinHeightInItems(int minHeightInItems) {
-        ViewGroup.LayoutParams lp = mMinHeightSpacer.getLayoutParams();
-        lp.height = minHeightInItems * getResources().getDimensionPixelSize(
-                R.dimen.qs_detail_item_height);
-        mMinHeightSpacer.setLayoutParams(lp);
-    }
-
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -153,65 +138,82 @@
     }
 
     private void handleSetItems(Item[] items) {
-        final int itemCount = items != null ? Math.min(items.length, mMaxItems) : 0;
+        final int itemCount = items != null ? items.length : 0;
         mEmpty.setVisibility(itemCount == 0 ? VISIBLE : GONE);
-        mItems.setVisibility(itemCount == 0 ? GONE : VISIBLE);
-        for (int i = mItems.getChildCount() - 1; i >= itemCount; i--) {
-            mItems.removeViewAt(i);
-        }
-        for (int i = 0; i < itemCount; i++) {
-            bind(items[i], mItems.getChildAt(i));
-        }
+        mItemList.setVisibility(itemCount == 0 ? GONE : VISIBLE);
+        mItems = items;
+        mAdapter.notifyDataSetChanged();
     }
 
     private void handleSetItemsVisible(boolean visible) {
         if (mItemsVisible == visible) return;
         mItemsVisible = visible;
-        for (int i = 0; i < mItems.getChildCount(); i++) {
-            mItems.getChildAt(i).setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
+        for (int i = 0; i < mItemList.getChildCount(); i++) {
+            mItemList.getChildAt(i).setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
         }
     }
 
-    private void bind(final Item item, View view) {
-        if (view == null) {
-            view = LayoutInflater.from(mContext).inflate(R.layout.qs_detail_item, this, false);
-            mItems.addView(view);
+    private class Adapter extends BaseAdapter {
+
+        @Override
+        public int getCount() {
+            return mItems != null ? mItems.length : 0;
         }
-        view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
-        final ImageView iv = (ImageView) view.findViewById(android.R.id.icon);
-        iv.setImageResource(item.icon);
-        iv.getOverlay().clear();
-        if (item.overlay != null) {
-            item.overlay.setBounds(0, 0, item.overlay.getIntrinsicWidth(),
-                    item.overlay.getIntrinsicHeight());
-            iv.getOverlay().add(item.overlay);
+
+        @Override
+        public Object getItem(int position) {
+            return mItems[position];
         }
-        final TextView title = (TextView) view.findViewById(android.R.id.title);
-        title.setText(item.line1);
-        final TextView summary = (TextView) view.findViewById(android.R.id.summary);
-        final boolean twoLines = !TextUtils.isEmpty(item.line2);
-        title.setMaxLines(twoLines ? 1 : 2);
-        summary.setVisibility(twoLines ? VISIBLE : GONE);
-        summary.setText(twoLines ? item.line2 : null);
-        view.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mCallback != null) {
-                    mCallback.onDetailItemClick(item);
-                }
+
+        @Override
+        public long getItemId(int position) {
+            return 0;
+        }
+
+        @Override
+        public View getView(int position, View view, ViewGroup parent) {
+            final Item item = mItems[position];
+            if (view == null) {
+                view = LayoutInflater.from(mContext).inflate(R.layout.qs_detail_item, parent,
+                        false);
             }
-        });
-        final ImageView disconnect = (ImageView) view.findViewById(android.R.id.icon2);
-        disconnect.setVisibility(item.canDisconnect ? VISIBLE : GONE);
-        disconnect.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mCallback != null) {
-                    mCallback.onDetailItemDisconnect(item);
-                }
+            view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
+            final ImageView iv = (ImageView) view.findViewById(android.R.id.icon);
+            iv.setImageResource(item.icon);
+            iv.getOverlay().clear();
+            if (item.overlay != null) {
+                item.overlay.setBounds(0, 0, item.overlay.getIntrinsicWidth(),
+                        item.overlay.getIntrinsicHeight());
+                iv.getOverlay().add(item.overlay);
             }
-        });
-    }
+            final TextView title = (TextView) view.findViewById(android.R.id.title);
+            title.setText(item.line1);
+            final TextView summary = (TextView) view.findViewById(android.R.id.summary);
+            final boolean twoLines = !TextUtils.isEmpty(item.line2);
+            title.setMaxLines(twoLines ? 1 : 2);
+            summary.setVisibility(twoLines ? VISIBLE : GONE);
+            summary.setText(twoLines ? item.line2 : null);
+            view.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mCallback != null) {
+                        mCallback.onDetailItemClick(item);
+                    }
+                }
+            });
+            final ImageView disconnect = (ImageView) view.findViewById(android.R.id.icon2);
+            disconnect.setVisibility(item.canDisconnect ? VISIBLE : GONE);
+            disconnect.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mCallback != null) {
+                        mCallback.onDetailItemDisconnect(item);
+                    }
+                }
+            });
+            return view;
+        }
+    };
 
     private class H extends Handler {
         private static final int SET_ITEMS = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 55eda98..6969e25 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -24,6 +24,7 @@
     private int mCellMargin;
 
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
+    private int mCellMarginTop;
 
     public TileLayout(Context context) {
         this(context, null);
@@ -60,9 +61,10 @@
         final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
         mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
         mCellMargin = res.getDimensionPixelSize(R.dimen.qs_tile_margin);
+        mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
         if (mColumns != columns) {
             mColumns = columns;
-            postInvalidate();
+            requestLayout();
             return true;
         }
         return false;
@@ -81,7 +83,8 @@
             record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
             previousView = record.tileView.updateAccessibilityOrder(previousView);
         }
-        setMeasuredDimension(width, (mCellHeight + mCellMargin) * rows);
+        setMeasuredDimension(width,
+                (mCellHeight + mCellMargin) * rows + (mCellMarginTop - mCellMargin));
     }
 
     private static int exactly(int size) {
@@ -114,7 +117,7 @@
     }
 
     private int getRowTop(int row) {
-        return row * (mCellHeight + mCellMargin) + mCellMargin;
+        return row * (mCellHeight + mCellMargin) + mCellMarginTop;
     }
 
     private int getColumnStart(int column) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 1fef8f1..63c85db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -216,7 +216,6 @@
             mItems.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty,
                     R.string.quick_settings_bluetooth_detail_empty_text);
             mItems.setCallback(this);
-            mItems.setMinHeightInItems(0);
             updateItems();
             setItemsVisible(mState.value);
             return mItems;