QS: Make user switcher expand when users don't fit

Also updates the switcher to match the latest redlines.

Bug: 16406694
Change-Id: Ibf44ed9ea2ef4e3c467724eb4c79f1df5b3e49f4
This commit is contained in:
Adrian Roos
2014-08-07 20:54:12 +02:00
parent dd06d04064
commit 1940892d89
12 changed files with 296 additions and 76 deletions

View File

@@ -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

View File

@@ -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>
<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>

View File

@@ -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"

View File

@@ -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>
sysui:verticalSpacing="4dp"
sysui:horizontalSpacing="4dp"
style="@style/UserDetailView" />

View File

@@ -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"/>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);
}
}
}
}

View File

@@ -326,8 +326,11 @@ public class QSPanel extends ViewGroup {
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) {

View File

@@ -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 class UserDetailView extends GridView {
}
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 @@ public class UserDetailView extends GridView {
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 @@ public class UserDetailView extends GridView {
v.setTag(item);
return v;
}
@Override
public void onClick(View view) {
UserSwitcherController.UserRecord tag =
(UserSwitcherController.UserRecord) view.getTag();
switchTo(tag);
}
}
}

View File

@@ -465,12 +465,12 @@ public class UserSwitcherController {
@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;
}