Merge "Implement new multi-user affordance."
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:baselineAligned="false"
|
||||
android:elevation="14dp"
|
||||
android:elevation="10dp"
|
||||
>
|
||||
|
||||
<View
|
||||
@@ -74,12 +74,22 @@
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="@dimen/status_bar_header_height"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:background="@null"
|
||||
android:scaleType="centerInside"
|
||||
android:padding="6dp"
|
||||
/>
|
||||
|
||||
<FrameLayout android:id="@+id/system_icons_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/status_bar_header_height"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_toStartOf="@id/multi_user_switch"
|
||||
android:layout_marginEnd="4dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_debug_info"
|
||||
android:visibility="invisible"
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#dd000000">
|
||||
android:background="#dd000000"
|
||||
android:elevation="12dp">
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.phone;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.settings.UserSwitcherHostView;
|
||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
|
||||
/**
|
||||
* Image button for the multi user switcher.
|
||||
*/
|
||||
public class MultiUserSwitch extends ImageButton implements View.OnClickListener,
|
||||
UserInfoController.OnUserInfoChangedListener {
|
||||
|
||||
private ViewGroup mOverlayParent;
|
||||
|
||||
public MultiUserSwitch(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
setOnClickListener(this);
|
||||
}
|
||||
|
||||
public void setOverlayParent(ViewGroup parent) {
|
||||
mOverlayParent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final UserManager um = UserManager.get(getContext());
|
||||
if (um.isUserSwitcherEnabled()) {
|
||||
final UserSwitcherHostView switcher =
|
||||
(UserSwitcherHostView) LayoutInflater.from(getContext()).inflate(
|
||||
R.layout.user_switcher_host, mOverlayParent, false);
|
||||
switcher.setFinishRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mOverlayParent.removeView(switcher);
|
||||
}
|
||||
});
|
||||
switcher.refreshUsers();
|
||||
mOverlayParent.addView(switcher);
|
||||
} else {
|
||||
Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
|
||||
getContext(), v, ContactsContract.Profile.CONTENT_URI,
|
||||
ContactsContract.QuickContact.MODE_LARGE, null);
|
||||
getContext().startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
|
||||
}
|
||||
}
|
||||
|
||||
public void setUserInfoController(UserInfoController userInfoController) {
|
||||
userInfoController.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserInfoChanged(String name, Drawable picture) {
|
||||
setImageDrawable(picture);
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,7 @@ public class NotificationPanelView extends PanelView implements
|
||||
super.onFinishInflate();
|
||||
mHeader = (StatusBarHeaderView) findViewById(R.id.header);
|
||||
mHeader.getBackgroundView().setOnClickListener(this);
|
||||
mHeader.setOverlayParent(this);
|
||||
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
|
||||
mStackScrollerContainer = findViewById(R.id.notification_container_parent);
|
||||
mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container);
|
||||
|
||||
@@ -111,6 +111,7 @@ import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
|
||||
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.UserInfoController;
|
||||
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
|
||||
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
|
||||
import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
|
||||
@@ -184,6 +185,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
LocationController mLocationController;
|
||||
NetworkController mNetworkController;
|
||||
RotationLockController mRotationLockController;
|
||||
UserInfoController mUserInfoController;
|
||||
|
||||
int mNaturalBarHeight = -1;
|
||||
int mIconSize = -1;
|
||||
@@ -670,6 +672,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
|| QuickSettings.DEBUG_GONE_TILES) {
|
||||
mRotationLockController = new RotationLockController(mContext);
|
||||
}
|
||||
mUserInfoController = new UserInfoController(mContext);
|
||||
final SignalClusterView signalCluster =
|
||||
(SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
|
||||
|
||||
@@ -737,6 +740,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
mQS = null; // fly away, be free
|
||||
}
|
||||
|
||||
// User info. Trigger first load.
|
||||
mHeader.setUserInfoController(mUserInfoController);
|
||||
mUserInfoController.reloadUserInfo();
|
||||
|
||||
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||
mBroadcastReceiver.onReceive(mContext,
|
||||
new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
|
||||
/**
|
||||
* The view to manage the header area in the expanded status bar.
|
||||
@@ -35,6 +36,7 @@ public class StatusBarHeaderView extends RelativeLayout {
|
||||
private ViewGroup mSystemIconsContainer;
|
||||
private View mDateTime;
|
||||
private View mKeyguardCarrierText;
|
||||
private MultiUserSwitch mMultiUserSwitch;
|
||||
|
||||
private int mCollapsedHeight;
|
||||
private int mExpandedHeight;
|
||||
@@ -53,6 +55,7 @@ public class StatusBarHeaderView extends RelativeLayout {
|
||||
mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
|
||||
mDateTime = findViewById(R.id.datetime);
|
||||
mKeyguardCarrierText = findViewById(R.id.keyguard_carrier_text);
|
||||
mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
|
||||
loadDimens();
|
||||
}
|
||||
|
||||
@@ -97,6 +100,11 @@ public class StatusBarHeaderView extends RelativeLayout {
|
||||
lp.height = systemIconsContainerHeight;
|
||||
mSystemIconsContainer.setLayoutParams(lp);
|
||||
}
|
||||
lp = mMultiUserSwitch.getLayoutParams();
|
||||
if (lp.height != systemIconsContainerHeight) {
|
||||
lp.height = systemIconsContainerHeight;
|
||||
mMultiUserSwitch.setLayoutParams(lp);
|
||||
}
|
||||
}
|
||||
|
||||
public void setExpansion(float height) {
|
||||
@@ -133,4 +141,12 @@ public class StatusBarHeaderView extends RelativeLayout {
|
||||
}
|
||||
updateHeights();
|
||||
}
|
||||
|
||||
public void setUserInfoController(UserInfoController userInfoController) {
|
||||
mMultiUserSwitch.setUserInfoController(userInfoController);
|
||||
}
|
||||
|
||||
public void setOverlayParent(ViewGroup parent) {
|
||||
mMultiUserSwitch.setOverlayParent(parent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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 android.app.ActivityManagerNative;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.ContactsContract;
|
||||
import android.security.KeyChain;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.internal.view.RotationPolicy;
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public final class UserInfoController {
|
||||
|
||||
private static final String TAG = "UserInfoController";
|
||||
|
||||
private final Context mContext;
|
||||
private final ArrayList<OnUserInfoChangedListener> mCallbacks =
|
||||
new ArrayList<OnUserInfoChangedListener>();
|
||||
private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask;
|
||||
|
||||
private boolean mUseDefaultAvatar;
|
||||
private String mUserName;
|
||||
private Drawable mUserDrawable;
|
||||
|
||||
public UserInfoController(Context context) {
|
||||
mContext = context;
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_USER_SWITCHED);
|
||||
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
|
||||
mContext.registerReceiver(mReceiver, filter);
|
||||
|
||||
IntentFilter profileFilter = new IntentFilter();
|
||||
profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
|
||||
profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
|
||||
mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
|
||||
null, null);
|
||||
}
|
||||
|
||||
public void addListener(OnUserInfoChangedListener callback) {
|
||||
mCallbacks.add(callback);
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
|
||||
reloadUserInfo();
|
||||
} else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
|
||||
if (mUseDefaultAvatar) {
|
||||
reloadUserInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
|
||||
Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
|
||||
try {
|
||||
final int currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
|
||||
final int changedUser =
|
||||
intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
|
||||
if (changedUser == currentUser) {
|
||||
reloadUserInfo();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Couldn't get current user id for profile change", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void reloadUserInfo() {
|
||||
if (mUserInfoTask != null) {
|
||||
mUserInfoTask.cancel(false);
|
||||
mUserInfoTask = null;
|
||||
}
|
||||
queryForUserInformation();
|
||||
}
|
||||
|
||||
private Bitmap circularClip(Bitmap input) {
|
||||
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;
|
||||
}
|
||||
|
||||
private void queryForUserInformation() {
|
||||
Context currentUserContext;
|
||||
UserInfo userInfo;
|
||||
try {
|
||||
userInfo = ActivityManagerNative.getDefault().getCurrentUser();
|
||||
currentUserContext = mContext.createPackageContextAsUser("android", 0,
|
||||
new UserHandle(userInfo.id));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "Couldn't create user context", e);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Couldn't get user info", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
final int userId = userInfo.id;
|
||||
final String userName = userInfo.name;
|
||||
|
||||
final Context context = currentUserContext;
|
||||
mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
|
||||
@Override
|
||||
protected Pair<String, Drawable> doInBackground(Void... params) {
|
||||
final UserManager um = UserManager.get(mContext);
|
||||
|
||||
// Fall back to the UserManager nickname if we can't read the name from the local
|
||||
// profile below.
|
||||
String name = userName;
|
||||
Drawable avatar = null;
|
||||
Bitmap rawAvatar = um.getUserIcon(userId);
|
||||
if (rawAvatar != null) {
|
||||
avatar = new BitmapDrawable(mContext.getResources(), circularClip(rawAvatar));
|
||||
} else {
|
||||
avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user);
|
||||
mUseDefaultAvatar = true;
|
||||
}
|
||||
|
||||
// If it's a single-user device, get the profile name, since the nickname is not
|
||||
// usually valid
|
||||
if (um.getUsers().size() <= 1) {
|
||||
// Try and read the display name from the local profile
|
||||
final Cursor cursor = context.getContentResolver().query(
|
||||
ContactsContract.Profile.CONTENT_URI, new String[] {
|
||||
ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME},
|
||||
null, null, null);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
name = cursor.getString(cursor.getColumnIndex(
|
||||
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Pair<String, Drawable>(name, avatar);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Pair<String, Drawable> result) {
|
||||
mUserName = result.first;
|
||||
mUserDrawable = result.second;
|
||||
mUserInfoTask = null;
|
||||
notifyChanged();
|
||||
}
|
||||
};
|
||||
mUserInfoTask.execute();
|
||||
}
|
||||
|
||||
private void notifyChanged() {
|
||||
for (OnUserInfoChangedListener listener : mCallbacks) {
|
||||
listener.onUserInfoChanged(mUserName, mUserDrawable);
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnUserInfoChangedListener {
|
||||
public void onUserInfoChanged(String name, Drawable picture);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user