QS: Make user switcher expand when users don't fit
Also updates the switcher to match the latest redlines.
Bug: 16406694
Change-Id: Ibf44ed9ea2ef4e3c467724eb4c79f1df5b3e49f4
diff --git a/packages/SystemUI/res/layout/data_usage.xml b/packages/SystemUI/res/layout/data_usage.xml
index 63d22b2..8831a05 100644
--- a/packages/SystemUI/res/layout/data_usage.xml
+++ b/packages/SystemUI/res/layout/data_usage.xml
@@ -17,6 +17,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
android:orientation="vertical" >
<TextView
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index c6a7368..5869bf3 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -14,39 +14,43 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/qs_detail_background"
- android:padding="16dp" >
-
- <TextView
- android:id="@android:id/button1"
- style="@style/QSBorderlessButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- 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"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_above="@android:id/button1" />
+ android:background="@drawable/qs_detail_background"
+ android:paddingBottom="16dp"
+ android:orientation="vertical">
-</RelativeLayout>
\ No newline at end of file
+ <FrameLayout
+ android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingEnd="16dp"
+ android:gravity="end">
+
+ <TextView
+ android:id="@android:id/button2"
+ style="@style/QSBorderlessButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:minWidth="132dp"
+ android:text="@string/quick_settings_more_settings"
+ android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+
+ <TextView
+ android:id="@android:id/button1"
+ style="@style/QSBorderlessButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="88dp"
+ android:text="@string/quick_settings_done"
+ android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_detail_items.xml b/packages/SystemUI/res/layout/qs_detail_items.xml
index b64005f..f61a43c 100644
--- a/packages/SystemUI/res/layout/qs_detail_items.xml
+++ b/packages/SystemUI/res/layout/qs_detail_items.xml
@@ -17,7 +17,10 @@
<!-- extends FrameLayout -->
<com.android.systemui.qs.QSDetailItems xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent"
+ android:paddingTop="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp">
<LinearLayout
android:id="@android:id/list"
diff --git a/packages/SystemUI/res/layout/qs_user_detail.xml b/packages/SystemUI/res/layout/qs_user_detail.xml
index 1d6df61..91d3a53 100644
--- a/packages/SystemUI/res/layout/qs_user_detail.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail.xml
@@ -16,14 +16,12 @@
~ limitations under the License
-->
-<!-- GridView -->
+<!-- PseudoGridView -->
<com.android.systemui.qs.tiles.UserDetailView
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:verticalSpacing="4dp"
- android:horizontalSpacing="4dp"
- android:numColumns="3"
- android:listSelector="@drawable/ripple_drawable">
-
-</com.android.systemui.qs.tiles.UserDetailView>
\ No newline at end of file
+ sysui:verticalSpacing="4dp"
+ sysui:horizontalSpacing="4dp"
+ style="@style/UserDetailView" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 5ceed84..2322f16 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -25,16 +25,17 @@
android:orientation="vertical"
android:gravity="top|center_horizontal"
android:paddingTop="16dp"
- android:paddingBottom="20dp"
+ android:minHeight="112dp"
android:clipChildren="false"
android:clipToPadding="false"
+ android:background="@drawable/ripple_drawable"
systemui:activatedFontFamily="sans-serif-medium">
<com.android.systemui.statusbar.phone.UserAvatarView
android:id="@+id/user_picture"
android:layout_width="@dimen/max_avatar_size"
android:layout_height="@dimen/max_avatar_size"
- android:layout_marginBottom="12dp"
+ android:layout_marginBottom="10dp"
systemui:frameWidth="2dp"
systemui:framePadding="6dp"
systemui:activeFrameColor="@color/current_user_border_color"/>
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index 5daf08e..9e5b1d6 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -34,4 +34,8 @@
<item name="android:layout_height">@dimen/search_panel_scrim_height</item>
<item name="android:layout_gravity">bottom</item>
</style>
+
+ <style name="UserDetailView">
+ <item name="numColumns">4</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 8bd3c39..6ecdca3 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -67,5 +67,10 @@
<declare-styleable name="DateView">
<attr name="datePattern" format="string" />
</declare-styleable>
+ <declare-styleable name="PseudoGridView">
+ <attr name="numColumns" format="integer" />
+ <attr name="verticalSpacing" format="dimension" />
+ <attr name="horizontalSpacing" format="dimension" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5db6912..baaa379 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -289,4 +289,8 @@
<item name="android:layout_height">@dimen/search_panel_scrim_height</item>
<item name="android:layout_gravity">bottom</item>
</style>
+
+ <style name="UserDetailView">
+ <item name="numColumns">3</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
new file mode 100644
index 0000000..d58663d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.qs;
+
+import com.android.systemui.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * A view that arranges it's children in a grid with a fixed number of evenly spaced columns.
+ *
+ * {@see android.widget.GridView}
+ */
+public class PseudoGridView extends ViewGroup {
+
+ private int mNumColumns = 3;
+ private int mVerticalSpacing;
+ private int mHorizontalSpacing;
+
+ public PseudoGridView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PseudoGridView);
+
+ final int N = a.getIndexCount();
+ for (int i = 0; i < N; i++) {
+ int attr = a.getIndex(i);
+ switch (attr) {
+ case R.styleable.PseudoGridView_numColumns:
+ mNumColumns = a.getInt(attr, 3);
+ break;
+ case R.styleable.PseudoGridView_verticalSpacing:
+ mVerticalSpacing = a.getDimensionPixelSize(attr, 0);
+ break;
+ case R.styleable.PseudoGridView_horizontalSpacing:
+ mHorizontalSpacing = a.getDimensionPixelSize(attr, 0);
+ break;
+ }
+ }
+
+ a.recycle();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ if (widthMode == MeasureSpec.UNSPECIFIED) {
+ throw new UnsupportedOperationException("Needs a maximum width");
+ }
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+
+ int childWidth = (width - (mNumColumns - 1) * mHorizontalSpacing) / mNumColumns;
+ int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
+ int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ int totalHeight = 0;
+ int children = getChildCount();
+ int rows = (children + mNumColumns - 1) / mNumColumns;
+ for (int row = 0; row < rows; row++) {
+ int startOfRow = row * mNumColumns;
+ int endOfRow = Math.min(startOfRow + mNumColumns, children);
+ int maxHeight = 0;
+ for (int i = startOfRow; i < endOfRow; i++) {
+ View child = getChildAt(i);
+ child.measure(childWidthSpec, childHeightSpec);
+ maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
+ }
+ int maxHeightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
+ for (int i = startOfRow; i < endOfRow; i++) {
+ View child = getChildAt(i);
+ child.measure(childWidthSpec, maxHeightSpec);
+ }
+ totalHeight += maxHeight;
+ if (row > 0) {
+ totalHeight += mVerticalSpacing;
+ }
+ }
+
+ setMeasuredDimension(width, getDefaultSize(totalHeight, heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int children = getChildCount();
+ int rows = (children + mNumColumns - 1) / mNumColumns;
+ int y = 0;
+ for (int row = 0; row < rows; row++) {
+ int x = 0;
+ int maxHeight = 0;
+ int startOfRow = row * mNumColumns;
+ int endOfRow = Math.min(startOfRow + mNumColumns, children);
+ for (int i = startOfRow; i < endOfRow; i++) {
+ View child = getChildAt(i);
+ int width = child.getMeasuredWidth();
+ int height = child.getMeasuredHeight();
+ child.layout(x, y, x + width, y + height);
+ maxHeight = Math.max(maxHeight, height);
+ x += width + mHorizontalSpacing;
+ }
+ y += maxHeight;
+ if (row > 0) {
+ y += mVerticalSpacing;
+ }
+ }
+ }
+
+ /**
+ * Bridges between a ViewGroup and a BaseAdapter.
+ * <p>
+ * Usage: {@code ViewGroupAdapterBridge.link(viewGroup, adapter)}
+ * <br />
+ * After this call, the ViewGroup's children will be provided by the adapter.
+ */
+ public static class ViewGroupAdapterBridge extends DataSetObserver {
+
+ private final WeakReference<ViewGroup> mViewGroup;
+ private final BaseAdapter mAdapter;
+ private boolean mReleased;
+
+ public static void link(ViewGroup viewGroup, BaseAdapter adapter) {
+ new ViewGroupAdapterBridge(viewGroup, adapter);
+ }
+
+ private ViewGroupAdapterBridge(ViewGroup viewGroup, BaseAdapter adapter) {
+ mViewGroup = new WeakReference<>(viewGroup);
+ mAdapter = adapter;
+ mReleased = false;
+ mAdapter.registerDataSetObserver(this);
+ refresh();
+ }
+
+ private void refresh() {
+ if (mReleased) {
+ return;
+ }
+ ViewGroup viewGroup = mViewGroup.get();
+ if (viewGroup == null) {
+ release();
+ return;
+ }
+ final int childCount = viewGroup.getChildCount();
+ final int adapterCount = mAdapter.getCount();
+ final int N = Math.max(childCount, adapterCount);
+ for (int i = 0; i < N; i++) {
+ if (i < adapterCount) {
+ View oldView = null;
+ if (i < childCount) {
+ oldView = viewGroup.getChildAt(i);
+ }
+ View newView = mAdapter.getView(i, oldView, viewGroup);
+ if (oldView == null) {
+ // We ran out of existing views. Add it at the end.
+ viewGroup.addView(newView);
+ } else if (oldView != newView) {
+ // We couldn't rebind the view. Replace it.
+ viewGroup.removeViewAt(i);
+ viewGroup.addView(newView, i);
+ }
+ } else {
+ int lastIndex = viewGroup.getChildCount() - 1;
+ viewGroup.removeViewAt(lastIndex);
+ }
+ }
+ }
+
+ @Override
+ public void onChanged() {
+ refresh();
+ }
+
+ @Override
+ public void onInvalidated() {
+ release();
+ }
+
+ private void release() {
+ if (!mReleased) {
+ mReleased = true;
+ mAdapter.unregisterDataSetObserver(this);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 59f3b3d..3679b4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -326,8 +326,11 @@
if (mFooter.hasFooter()) {
h += mFooter.getView().getHeight();
}
- mDetail.measure(exactly(width), exactly(h));
- setMeasuredDimension(width, h);
+ mDetail.measure(exactly(width), MeasureSpec.UNSPECIFIED);
+ if (mDetail.getMeasuredHeight() < h) {
+ mDetail.measure(exactly(width), exactly(h));
+ }
+ setMeasuredDimension(width, Math.max(h, mDetail.getMeasuredHeight()));
}
private static int exactly(int size) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 8cff81a..c524edc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -17,45 +17,24 @@
package com.android.systemui.qs.tiles;
import com.android.systemui.R;
+import com.android.systemui.qs.PseudoGridView;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import android.content.Context;
-import android.content.Intent;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.GridView;
/**
* Quick settings detail view for user switching.
*/
-public class UserDetailView extends GridView {
+public class UserDetailView extends PseudoGridView {
- public UserDetailView(Context context) {
- this(context, null);
- }
+ private Adapter mAdapter;
public UserDetailView(Context context, AttributeSet attrs) {
- this(context, attrs, android.R.attr.gridViewStyle);
- }
-
- public UserDetailView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public UserDetailView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
-
- setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- UserSwitcherController.UserRecord tag =
- (UserSwitcherController.UserRecord) view.getTag();
- ((Adapter)getAdapter()).switchTo(tag);
- }
- });
+ super(context, attrs);
}
public static UserDetailView inflate(Context context, ViewGroup parent, boolean attach) {
@@ -64,10 +43,12 @@
}
public void createAndSetAdapter(UserSwitcherController controller) {
- setAdapter(new Adapter(mContext, controller));
+ mAdapter = new Adapter(mContext, controller);
+ ViewGroupAdapterBridge.link(this, mAdapter);
}
- public static class Adapter extends UserSwitcherController.BaseUserAdapter {
+ public static class Adapter extends UserSwitcherController.BaseUserAdapter
+ implements OnClickListener {
private Context mContext;
@@ -81,6 +62,9 @@
UserSwitcherController.UserRecord item = getItem(position);
UserDetailItemView v = UserDetailItemView.convertOrInflate(
mContext, convertView, parent);
+ if (v != convertView) {
+ v.setOnClickListener(this);
+ }
String name = getName(mContext, item);
if (item.picture == null) {
v.bind(name, getDrawable(mContext, item));
@@ -91,5 +75,12 @@
v.setTag(item);
return v;
}
+
+ @Override
+ public void onClick(View view) {
+ UserSwitcherController.UserRecord tag =
+ (UserSwitcherController.UserRecord) view.getTag();
+ switchTo(tag);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index c48f3f5..a1993f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -465,12 +465,12 @@
@Override
public View createDetailView(Context context, View convertView, ViewGroup parent) {
+ UserDetailView v;
if (!(convertView instanceof UserDetailView)) {
- convertView = UserDetailView.inflate(context, parent, false);
- }
- UserDetailView v = (UserDetailView) convertView;
- if (v.getAdapter() == null) {
+ v = UserDetailView.inflate(context, parent, false);
v.createAndSetAdapter(UserSwitcherController.this);
+ } else {
+ v = (UserDetailView) convertView;
}
return v;
}