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
This commit is contained in:
@@ -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>
|
||||
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>
|
||||
44
packages/SystemUI/res/layout/qs_user_detail_item.xml
Normal file
44
packages/SystemUI/res/layout/qs_user_detail_item.xml
Normal file
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -108,6 +108,9 @@ public class QSPanel extends ViewGroup {
|
||||
mHost = host;
|
||||
}
|
||||
|
||||
public QSTileHost getHost() {
|
||||
return mHost;
|
||||
}
|
||||
|
||||
public void updateResources() {
|
||||
final Resources res = mContext.getResources();
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user