diff options
author | Adrian Roos <roosa@google.com> | 2014-07-16 16:44:49 +0200 |
---|---|---|
committer | Adrian Roos <roosa@google.com> | 2014-07-16 16:25:30 +0000 |
commit | 00a0b1f397557790cf9ab55fe06e72a96ebc5353 (patch) | |
tree | 3a985f307dae2f7c24a321ee9c9e68c25e727739 /packages | |
parent | 09f1c724e3eae6d28883b7c16ef6531b556c5691 (diff) | |
download | frameworks_base-00a0b1f397557790cf9ab55fe06e72a96ebc5353.zip frameworks_base-00a0b1f397557790cf9ab55fe06e72a96ebc5353.tar.gz frameworks_base-00a0b1f397557790cf9ab55fe06e72a96ebc5353.tar.bz2 |
Implement real QS user switcher
Replaces the stop-gap user switcher with the real deal.
Dimensions may need some further adjustments.
Bug: 15545213
Change-Id: I4399635c03553dac935049d5b8297fe5f5c1dc9a
Diffstat (limited to 'packages')
13 files changed, 538 insertions, 439 deletions
diff --git a/packages/SystemUI/res/layout/qs_user_detail.xml b/packages/SystemUI/res/layout/qs_user_detail.xml index eedae9f..1d6df61 100644 --- a/packages/SystemUI/res/layout/qs_user_detail.xml +++ b/packages/SystemUI/res/layout/qs_user_detail.xml @@ -16,9 +16,14 @@ ~ limitations under the License --> -<com.android.systemui.qs.tiles.UserDetail +<!-- GridView --> +<com.android.systemui.qs.tiles.UserDetailView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent"> - <include layout="@layout/user_switcher_host" /> -</com.android.systemui.qs.tiles.UserDetail>
\ No newline at end of file + 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 diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml new file mode 100644 index 0000000..00b3645 --- /dev/null +++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml @@ -0,0 +1,44 @@ +<?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 + --> + +<com.android.systemui.qs.tiles.UserDetailItemView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="top|center_horizontal" + android:paddingTop="16dp" + android:paddingBottom="20dp"> + + <com.android.systemui.statusbar.phone.UserAvatarView + android:id="@+id/user_picture" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_marginBottom="12dp" + systemui:frameWidth="2dp" + systemui:activeFrameColor="@color/current_user_border_color"/> + + <TextView + android:id="@+id/user_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14sp" + android:text="@string/guest_nickname"/> + +</com.android.systemui.qs.tiles.UserDetailItemView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/user_switcher_host.xml b/packages/SystemUI/res/layout/user_switcher_host.xml deleted file mode 100644 index c1626c6..0000000 --- a/packages/SystemUI/res/layout/user_switcher_host.xml +++ /dev/null @@ -1,31 +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 - --> - -<!-- FrameLayout --> -<com.android.systemui.settings.UserSwitcherHostView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <ListView android:id="@android:id/list" - android:layout_width="match_parent" - android:layout_height="match_parent" - tools:listitem="@layout/user_switcher_item"/> - -</com.android.systemui.settings.UserSwitcherHostView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/user_switcher_item.xml b/packages/SystemUI/res/layout/user_switcher_item.xml deleted file mode 100644 index 8df2f5a..0000000 --- a/packages/SystemUI/res/layout/user_switcher_item.xml +++ /dev/null @@ -1,49 +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 - --> - -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="64dp" - android:orientation="horizontal" - android:gravity="center_vertical" - tools:context=".settings.UserSwitcherDialog"> - <ImageView - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_marginStart="4dp" - android:id="@+id/user_picture" - tools:src="@drawable/dessert_zombiegingerbread"/> - <TextView - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:id="@+id/user_name" - android:textAppearance="?android:attr/textAppearanceLarge" - android:padding="8dp" - android:gravity="center_vertical" - tools:text="Hiroshi Lockheimer" - /> - <ImageView - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_marginEnd="4dp" - android:src="@*android:drawable/ic_menu_delete" - android:id="@+id/user_delete" - android:background="?android:attr/selectableItemBackground"/> -</LinearLayout> diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 5f09cbd..4901f40 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -108,6 +108,9 @@ public class QSPanel extends ViewGroup { mHost = host; } + public QSTileHost getHost() { + return mHost; + } public void updateResources() { final Resources res = mContext.getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetail.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetail.java deleted file mode 100644 index a9a2724..0000000 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetail.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.qs.tiles; - -import com.android.systemui.R; -import com.android.systemui.qs.QSTile; - -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.FrameLayout; - -/** - * Quick settings detail view for user switching. - */ -public class UserDetail extends FrameLayout { - - static final Intent USER_SETTINGS_INTENT = new Intent("android.settings.USER_SETTINGS"); - - public UserDetail(Context context) { - this(context, null); - } - - public UserDetail(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public UserDetail(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public UserDetail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - public static QSTile.DetailAdapter USER_DETAIL_ADAPTER = new QSTile.DetailAdapter() { - @Override - public int getTitle() { - return R.string.quick_settings_user_title; - } - - @Override - public Boolean getToggleState() { - return null; - } - - @Override - public View createDetailView(Context context, View convertView, ViewGroup parent) { - return LayoutInflater.from(context).inflate(R.layout.qs_user_detail, parent, false); - } - - @Override - public Intent getSettingsIntent() { - return USER_SETTINGS_INTENT; - } - - @Override - public void setToggleState(boolean state) { - } - }; -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java new file mode 100644 index 0000000..d765aab --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.qs.tiles; + +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.UserAvatarView; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * Displays one user in the {@link UserDetailView} view. + */ +public class UserDetailItemView extends LinearLayout { + + private UserAvatarView mAvatar; + private TextView mName; + + public UserDetailItemView(Context context) { + this(context, null); + } + + public UserDetailItemView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public UserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public UserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public static UserDetailItemView convertOrInflate(Context context, View convertView, + ViewGroup root) { + if (!(convertView instanceof UserDetailItemView)) { + convertView = LayoutInflater.from(context).inflate( + R.layout.qs_user_detail_item, root, false); + } + return (UserDetailItemView) convertView; + } + + public void bind(String name, Bitmap picture) { + mName.setText(name); + mAvatar.setBitmap(picture); + } + + public void bind(String name, Drawable picture) { + mName.setText(name); + mAvatar.setDrawable(picture); + } + + @Override + protected void onFinishInflate() { + mAvatar = (UserAvatarView) findViewById(R.id.user_picture); + mName = (TextView) findViewById(R.id.user_name); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java new file mode 100644 index 0000000..ec5f28c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.qs.tiles; + +import com.android.systemui.R; +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 UserDetailView(Context context) { + this(context, null); + } + + 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); + } + }); + } + + public static UserDetailView inflate(Context context, ViewGroup parent, boolean attach) { + return (UserDetailView) LayoutInflater.from(context).inflate( + R.layout.qs_user_detail, parent, attach); + } + + public void createAndSetAdapter(UserSwitcherController controller) { + setAdapter(new Adapter(mContext, controller)); + } + + public static class Adapter extends UserSwitcherController.BaseUserAdapter { + + private Context mContext; + + public Adapter(Context context, UserSwitcherController controller) { + super(controller); + mContext = context; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + UserSwitcherController.UserRecord item = getItem(position); + UserDetailItemView v = UserDetailItemView.convertOrInflate( + mContext, convertView, parent); + String name; + if (item.isGuest) { + name = mContext.getString( + item.info == null ? R.string.guest_new_guest : R.string.guest_nickname); + } else { + name = item.info.name; + } + if (item.picture == null) { + v.bind(name, mContext.getDrawable(R.drawable.ic_account_circle)); + } else { + v.bind(name, item.picture); + } + v.setActivated(item.isCurrent); + v.setTag(item); + return v; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java deleted file mode 100644 index a5c5862..0000000 --- a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.settings; - -import com.android.systemui.R; - -import android.app.ActivityManagerNative; -import android.content.Context; -import android.content.pm.UserInfo; -import android.graphics.Bitmap; -import android.graphics.BitmapShader; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Shader; -import android.os.Handler; -import android.os.RemoteException; -import android.os.UserManager; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; -import android.util.AttributeSet; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManagerGlobal; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; - -import java.util.ArrayList; -import java.util.List; - -/** - * A quick and dirty view to show a user switcher. - */ -public class UserSwitcherHostView extends FrameLayout - implements ListView.OnItemClickListener, View.OnClickListener { - - private static final String TAG = "UserSwitcherDialog"; - - private ArrayList<UserInfo> mUserInfo = new ArrayList<UserInfo>(); - private UserInfo mGuestUser; - private Adapter mAdapter = new Adapter(); - private UserManager mUserManager; - private Runnable mFinishRunnable; - private ListView mListView; - private boolean mGuestUserEnabled; - - public UserSwitcherHostView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - if (isInEditMode()) { - return; - } - mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - - mGuestUserEnabled = Settings.Global.getInt(context.getContentResolver(), - Settings.Global.GUEST_USER_ENABLED, 0) == 1; - } - - public UserSwitcherHostView(Context context, AttributeSet attrs) { - this(context, attrs, com.android.internal.R.attr.listViewStyle); - } - - public UserSwitcherHostView(Context context) { - this(context, null); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mListView = (ListView) findViewById(android.R.id.list); - mListView.setAdapter(mAdapter); - mListView.setOnItemClickListener(this); - refreshUsers(); - } - - @Override - public void onItemClick(AdapterView<?> l, View v, int position, long id) { - // Last item is the guest - if (position == mUserInfo.size()) { - postDelayed(new Runnable() { - public void run() { - switchToGuestUser(); - } - }, 100); - } else { - final int userId = mAdapter.getItem(position).id; - postDelayed(new Runnable() { - public void run() { - switchUser(userId); - } - }, 100); - } - } - - @Override - public void onClick(View v) { - // Delete was clicked - postDelayed(new Runnable() { - public void run() { - if (mGuestUser != null) { - switchUser(0); - mUserManager.removeUser(mGuestUser.id); - mGuestUser = null; - refreshUsers(); - } - } - }, 100); - } - - private void switchUser(int userId) { - try { - WindowManagerGlobal.getWindowManagerService().lockNow(null); - ActivityManagerNative.getDefault().switchUser(userId); - finish(); - } catch (RemoteException e) { - Log.e(TAG, "Couldn't switch user.", e); - } - } - - private void switchToGuestUser() { - if (mGuestUser == null) { - // No guest user. Create one. - mGuestUser = mUserManager.createGuest(mContext, - mContext.getResources().getString(R.string.guest_nickname)); - } - switchUser(mGuestUser.id); - } - - private void finish() { - if (mFinishRunnable != null) { - mFinishRunnable.run(); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_UP) { - finish(); - } - return true; - } - - @Override - protected void onVisibilityChanged(View changedView, int visibility) { - super.onVisibilityChanged(changedView, visibility); - // A gross hack to get rid of the switcher when the shade is collapsed. - if (visibility != VISIBLE) { - finish(); - } - } - - public void setFinishRunnable(Runnable finishRunnable) { - mFinishRunnable = finishRunnable; - } - - public void refreshUsers() { - mUserInfo.clear(); - mGuestUser = null; - List<UserInfo> users = mUserManager.getUsers(true); - for (UserInfo user : users) { - if (user.isGuest()) { - mGuestUser = user; - } else if (!user.isManagedProfile()) { - mUserInfo.add(user); - } - } - mAdapter.notifyDataSetChanged(); - } - - private class Adapter extends BaseAdapter { - - @Override - public int getCount() { - return mUserInfo.size() + (mGuestUserEnabled ? 1 : 0); - } - - @Override - public UserInfo getItem(int position) { - if (position < mUserInfo.size()) { - return mUserInfo.get(position); - } else { - return mGuestUser; - } - } - - @Override - public long getItemId(int position) { - if (position < mUserInfo.size()) { - return getItem(position).serialNumber; - } else { - return mGuestUser != null ? mGuestUser.serialNumber : -1; - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null || (!(convertView.getTag() instanceof ViewHolder))) { - convertView = createView(parent); - } - ViewHolder h = (ViewHolder) convertView.getTag(); - bindView(h, getItem(position)); - return convertView; - } - - private View createView(ViewGroup parent) { - View v = LayoutInflater.from(getContext()).inflate( - R.layout.user_switcher_item, parent, false); - ViewHolder h = new ViewHolder(); - h.name = (TextView) v.findViewById(R.id.user_name); - h.picture = (ImageView) v.findViewById(R.id.user_picture); - h.delete = (ImageView) v.findViewById(R.id.user_delete); - v.setTag(h); - return v; - } - - private void bindView(ViewHolder h, UserInfo item) { - if (item != null) { - h.name.setText(item.name); - h.picture.setImageBitmap(circularClip(mUserManager.getUserIcon(item.id))); - h.delete.setVisibility(item.isGuest() ? View.VISIBLE : View.GONE); - h.delete.setOnClickListener(UserSwitcherHostView.this); - if (item.isGuest()) { - h.picture.setImageResource(R.drawable.ic_account_circle); - } - } else { - h.name.setText(R.string.guest_new_guest); - h.picture.setImageResource(R.drawable.ic_account_circle); - h.delete.setVisibility(View.GONE); - } - } - - private Bitmap circularClip(Bitmap input) { - if (input == null) { - return null; - } - Bitmap output = Bitmap.createBitmap(input.getWidth(), - input.getHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(output); - final Paint paint = new Paint(); - paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); - paint.setAntiAlias(true); - canvas.drawCircle(input.getWidth() / 2, input.getHeight() / 2, input.getWidth() / 2, - paint); - return output; - } - - class ViewHolder { - TextView name; - ImageView picture; - ImageView delete; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java index d32ad50..688c0d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java @@ -26,7 +26,7 @@ import android.view.View; import android.widget.FrameLayout; import com.android.systemui.qs.QSPanel; -import com.android.systemui.qs.tiles.UserDetail; +import com.android.systemui.qs.tiles.UserDetailView; /** * Container for image of the multi user switcher (tappable). @@ -53,7 +53,8 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener public void onClick(View v) { final UserManager um = UserManager.get(getContext()); if (um.isUserSwitcherEnabled()) { - mQsPanel.showDetailAdapter(true, UserDetail.USER_DETAIL_ADAPTER); + mQsPanel.showDetailAdapter(true, + mQsPanel.getHost().getUserSwitcherController().userDetailAdapter); } else { Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent( getContext(), v, ContactsContract.Profile.CONTENT_URI, 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 2c43161..a15a758 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -127,6 +127,7 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.LocationControllerImpl; import com.android.systemui.statusbar.policy.NetworkControllerImpl; import com.android.systemui.statusbar.policy.RotationLockControllerImpl; +import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener; @@ -194,6 +195,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, VolumeComponent mVolumeComponent; KeyguardUserSwitcher mKeyguardUserSwitcher; FlashlightController mFlashlightController; + UserSwitcherController mUserSwitcherController; int mNaturalBarHeight = -1; int mIconSize = -1; @@ -691,6 +693,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } mFlashlightController = new FlashlightController(mContext); + mUserSwitcherController = new UserSwitcherController(mContext); // Set up the quick settings tile panel mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel); @@ -698,7 +701,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, final QSTileHost qsh = new QSTileHost(mContext, this, mBluetoothController, mLocationController, mRotationLockController, mNetworkController, mZenModeController, null /*tethering*/, - mCastController, mVolumeComponent, mFlashlightController); + mCastController, mVolumeComponent, mFlashlightController, + mUserSwitcherController); mQSPanel.setHost(qsh); for (QSTile<?> tile : qsh.getTiles()) { mQSPanel.addTile(tile); @@ -2326,6 +2330,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (mCastController != null) { mCastController.dump(fd, pw, args); } + if (mUserSwitcherController != null) { + mUserSwitcherController.dump(fd, pw, args); + } } private String hunStateToString(Entry entry) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index 5fbade1..a599070 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -40,6 +40,7 @@ import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.policy.TetheringController; +import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.volume.VolumeComponent; @@ -63,12 +64,14 @@ public class QSTileHost implements QSTile.Host { private final VolumeComponent mVolume; private final ArrayList<QSTile<?>> mTiles = new ArrayList<QSTile<?>>(); private final FlashlightController mFlashlight; + private final UserSwitcherController mUserSwitcherController; public QSTileHost(Context context, PhoneStatusBar statusBar, BluetoothController bluetooth, LocationController location, RotationLockController rotation, NetworkController network, ZenModeController zen, TetheringController tethering, - CastController cast, VolumeComponent volume, FlashlightController flashlight) { + CastController cast, VolumeComponent volume, FlashlightController flashlight, + UserSwitcherController userSwitcher) { mContext = context; mStatusBar = statusBar; mBluetooth = bluetooth; @@ -80,6 +83,7 @@ public class QSTileHost implements QSTile.Host { mCast = cast; mVolume = volume; mFlashlight = flashlight; + mUserSwitcherController = userSwitcher; final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName()); ht.start(); @@ -181,4 +185,8 @@ public class QSTileHost implements QSTile.Host { public FlashlightController getFlashlightController() { return mFlashlight; } + + public UserSwitcherController getUserSwitcherController() { + return mUserSwitcherController; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java new file mode 100644 index 0000000..4640067 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -0,0 +1,279 @@ +/* + * 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 com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.qs.tiles.UserDetailView; + +import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.UserInfo; +import android.graphics.Bitmap; +import android.os.AsyncTask; +import android.os.RemoteException; +import android.os.UserManager; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManagerGlobal; +import android.widget.BaseAdapter; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +/** + * Keeps a list of all users on the device for user switching. + */ +public class UserSwitcherController { + + private static final String TAG = "UserSwitcherController"; + + private final Context mContext; + private final UserManager mUserManager; + private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>(); + + private ArrayList<UserRecord> mUsers = new ArrayList<>(); + + public UserSwitcherController(Context context) { + mContext = context; + mUserManager = UserManager.get(context); + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_ADDED); + filter.addAction(Intent.ACTION_USER_REMOVED); + filter.addAction(Intent.ACTION_USER_INFO_CHANGED); + filter.addAction(Intent.ACTION_USER_SWITCHED); + mContext.registerReceiver(mReceiver, filter); + refreshUsers(); + } + + private void refreshUsers() { + new AsyncTask<Void, Void, ArrayList<UserRecord>>() { + + @Override + protected ArrayList<UserRecord> doInBackground(Void... params) { + List<UserInfo> infos = mUserManager.getUsers(true); + if (infos == null) { + return null; + } + ArrayList<UserRecord> records = new ArrayList<>(infos.size()); + int currentId = ActivityManager.getCurrentUser(); + UserRecord guestRecord = null; + + for (UserInfo info : infos) { + boolean isCurrent = currentId == info.id; + if (info.isGuest()) { + guestRecord = new UserRecord(info, null /* picture */, + true /* isGuest */, isCurrent); + } else if (!info.isManagedProfile()) { + records.add(new UserRecord(info, mUserManager.getUserIcon(info.id), + false /* isGuest */, isCurrent)); + } + } + + if (guestRecord == null) { + records.add(new UserRecord(null /* info */, null /* picture */, + true /* isGuest */, false /* isCurrent */)); + } else { + records.add(guestRecord); + } + + return records; + } + + @Override + protected void onPostExecute(ArrayList<UserRecord> userRecords) { + if (userRecords != null) { + mUsers = userRecords; + notifyAdapters(); + } + } + }.execute((Void[])null); + } + + private void notifyAdapters() { + for (int i = mAdapters.size() - 1; i >= 0; i--) { + BaseUserAdapter adapter = mAdapters.get(i).get(); + if (adapter != null) { + adapter.notifyDataSetChanged(); + } else { + mAdapters.remove(i); + } + } + } + + public void switchTo(UserRecord record) { + int id; + if (record.isGuest && record.info == null) { + // No guest user. Create one. + id = mUserManager.createGuest(mContext, + mContext.getResources().getString(R.string.guest_nickname)).id; + } else { + id = record.info.id; + } + + if (ActivityManager.getCurrentUser() == id) { + return; + } + + try { + WindowManagerGlobal.getWindowManagerService().lockNow(null); + ActivityManagerNative.getDefault().switchUser(id); + } catch (RemoteException e) { + Log.e(TAG, "Couldn't switch user.", e); + } + } + + private BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { + final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + final int N = mUsers.size(); + for (int i = 0; i < N; i++) { + UserRecord record = mUsers.get(i); + boolean shouldBeCurrent = record.info.id == currentId; + if (record.isCurrent != shouldBeCurrent) { + mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent)); + } + } + notifyAdapters(); + } else { + refreshUsers(); + } + } + }; + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("UserSwitcherController state:"); + pw.print(" mUsers.size="); pw.println(mUsers.size()); + for (int i = 0; i < mUsers.size(); i++) { + final UserRecord u = mUsers.get(i); + pw.print(" "); pw.println(u.toString()); + } + } + + public static abstract class BaseUserAdapter extends BaseAdapter { + + final UserSwitcherController mController; + + protected BaseUserAdapter(UserSwitcherController controller) { + mController = controller; + controller.mAdapters.add(new WeakReference<>(this)); + } + + @Override + public int getCount() { + return mController.mUsers.size(); + } + + @Override + public UserRecord getItem(int position) { + return mController.mUsers.get(position); + } + + @Override + public long getItemId(int position) { + return mController.mUsers.get(position).info.id; + } + + public void switchTo(UserRecord record) { + mController.switchTo(record); + } + } + + public static final class UserRecord { + public final UserInfo info; + public final Bitmap picture; + public final boolean isGuest; + public final boolean isCurrent; + + public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent) { + this.info = info; + this.picture = picture; + this.isGuest = isGuest; + this.isCurrent = isCurrent; + } + + public UserRecord copyWithIsCurrent(boolean _isCurrent) { + return new UserRecord(info, picture, isGuest, _isCurrent); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("UserRecord("); + if (info != null) { + sb.append("name=\"" + info.name + "\" id=" + info.id); + } else { + sb.append("<add guest placeholder>"); + } + if (isGuest) { + sb.append(" <isGuest>"); + } + if (isCurrent) { + sb.append(" <isCurrent>"); + } + if (picture != null) { + sb.append(" <hasPicture>"); + } + sb.append(')'); + return sb.toString(); + } + } + + public final QSTile.DetailAdapter userDetailAdapter = new QSTile.DetailAdapter() { + private final Intent USER_SETTINGS_INTENT = new Intent("android.settings.USER_SETTINGS"); + + @Override + public int getTitle() { + return R.string.quick_settings_user_title; + } + + @Override + public View createDetailView(Context context, View convertView, ViewGroup parent) { + if (!(convertView instanceof UserDetailView)) { + convertView = UserDetailView.inflate(context, parent, false); + } + UserDetailView v = (UserDetailView) convertView; + if (v.getAdapter() == null) { + v.createAndSetAdapter(UserSwitcherController.this); + } + return v; + } + + @Override + public Intent getSettingsIntent() { + return USER_SETTINGS_INTENT; + } + + @Override + public Boolean getToggleState() { + return null; + } + + @Override + public void setToggleState(boolean state) { + } + }; +} |