Merge "Second pass on Keyguard multi-user switcher" into jb-mr1-dev

This commit is contained in:
Adam Cohen
2012-09-16 14:46:54 -07:00
committed by Android (Google) Code Review
10 changed files with 428 additions and 63 deletions

View File

@@ -17,9 +17,6 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="100%p" android:toXDelta="0"
android:duration="500"
android:interpolator="@interpolator/decelerate_quad" />
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="500"
android:interpolator="@interpolator/decelerate_quad" />

View File

@@ -17,9 +17,6 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="-100%p"
android:duration="500"
android:interpolator="@interpolator/decelerate_quad" />
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="500"
android:interpolator="@interpolator/decelerate_quad" />

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2012 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/activity_picker_bg_activated" />
</selector>

View File

@@ -23,9 +23,11 @@
android:layout_width="125dp"
android:layout_height="125dp"
android:background="#550000ff"
android:gravity="center_horizontal">
android:gravity="center_horizontal"
android:foreground="@drawable/kg_avatar_overlay">
<ImageView
android:id="@+id/keyguard_user_avatar"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>

View File

@@ -19,23 +19,15 @@
<com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="375dp"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<include
android:id="@+id/keyguard_active_user"
android:layout_width="250dp"
android:layout_height="250dp"
layout="@layout/keyguard_multi_user_avatar"/>
<com.android.internal.policy.impl.keyguard.KeyguardSubdivisionLayout
android:id="@+id/keyguard_users_grid"
android:orientation="horizontal"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center" />
<ScrollView
android:layout_width="125dp"
android:layout_height="250dp">
<LinearLayout
android:id="@+id/keyguard_inactive_users"
android:orientation="vertical"
layout_width="match_parent"
layout_height="wrap_content"/>
</ScrollView>
</com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView>

View File

@@ -1260,10 +1260,9 @@
<java-symbol type="id" name="pin_delete_button" />
<java-symbol type="id" name="keyguard_user_avatar" />
<java-symbol type="id" name="keyguard_user_name" />
<java-symbol type="id" name="keyguard_active_user" />
<java-symbol type="id" name="keyguard_inactive_users" />
<java-symbol type="id" name="keyguard_transport_control" />
<java-symbol type="id" name="keyguard_status_view" />
<java-symbol type="id" name="keyguard_users_grid" />
<java-symbol type="integer" name="config_carDockRotation" />
<java-symbol type="integer" name="config_defaultUiModeType" />
<java-symbol type="integer" name="config_deskDockRotation" />

View File

@@ -86,6 +86,11 @@ public class KeyguardHostView extends KeyguardViewBase {
void show();
}
/*package*/ interface UserSwitcherCallback {
void hideSecurityView(int duration);
void showSecurityView();
}
public KeyguardHostView(Context context) {
this(context, null);
}
@@ -691,11 +696,27 @@ public class KeyguardHostView extends KeyguardViewBase {
List<UserInfo> users = mUm.getUsers();
if (users.size() > 1) {
KeyguardWidgetFrame userswitcher = (KeyguardWidgetFrame)
KeyguardWidgetFrame userSwitcher = (KeyguardWidgetFrame)
LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
mAppWidgetContainer, false);
// add the switcher to the left of status view
mAppWidgetContainer.addView(userswitcher, getWidgetPosition(R.id.keyguard_status_view));
// add the switcher in the first position
mAppWidgetContainer.addView(userSwitcher, getWidgetPosition(R.id.keyguard_status_view));
KeyguardMultiUserSelectorView multiUser = (KeyguardMultiUserSelectorView)
userSwitcher.getChildAt(0);
UserSwitcherCallback callback = new UserSwitcherCallback() {
@Override
public void hideSecurityView(int duration) {
mSecurityViewContainer.animate().alpha(0).setDuration(duration);
}
@Override
public void showSecurityView() {
mSecurityViewContainer.setAlpha(1.0f);
}
};
multiUser.setCallback(callback);
}
}

View File

@@ -16,16 +16,17 @@
package com.android.internal.policy.impl.keyguard;
import android.app.ActivityManagerNative;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.pm.UserInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManagerGlobal;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -33,11 +34,15 @@ import android.widget.TextView;
import com.android.internal.R;
class KeyguardMultiUserAvatar extends FrameLayout {
private static final String TAG = "KeyguardViewHost";
private ImageView mUserImage;
private TextView mUserName;
private UserInfo mUserInfo;
private static final int INACTIVE_COLOR = 85;
private static final int INACTIVE_ALPHA = 195;
private static final float ACTIVE_SCALE = 1.1f;
private boolean mActive;
private boolean mInit = true;
private KeyguardMultiUserSelectorView mUserSelector;
public static KeyguardMultiUserAvatar fromXml(int resId, Context context,
@@ -73,17 +78,86 @@ class KeyguardMultiUserAvatar extends FrameLayout {
mUserImage.setImageDrawable(Drawable.createFromPath(mUserInfo.iconPath));
mUserName.setText(mUserInfo.name);
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
ActivityManagerNative.getDefault().switchUser(mUserInfo.id);
WindowManagerGlobal.getWindowManagerService().lockNow();
mUserSelector.init();
} catch (RemoteException re) {
Log.e(TAG, "Couldn't switch user " + re);
setOnClickListener(mUserSelector);
setActive(false, false, 0, null);
mInit = false;
}
public void setActive(boolean active, boolean animate, int duration, final Runnable onComplete) {
if (mActive != active || mInit) {
mActive = active;
final int finalFilterAlpha = mActive ? 0 : INACTIVE_ALPHA;
final int initFilterAlpha = mActive ? INACTIVE_ALPHA : 0;
final float finalScale = mActive ? ACTIVE_SCALE : 1.0f;
final float initScale = mActive ? 1.0f : ACTIVE_SCALE;
if (active) {
KeyguardSubdivisionLayout parent = (KeyguardSubdivisionLayout) getParent();
parent.setTopChild(parent.indexOfChild(this));
}
if (animate) {
ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
va.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float r = animation.getAnimatedFraction();
float scale = (1 - r) * initScale + r * finalScale;
int filterAlpha = (int) ((1 - r) * initFilterAlpha + r * finalFilterAlpha);
setScaleX(scale);
setScaleY(scale);
mUserImage.setColorFilter(Color.argb(filterAlpha, INACTIVE_COLOR,
INACTIVE_COLOR, INACTIVE_COLOR));
mUserSelector.invalidate();
}
});
va.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (onComplete != null) {
onComplete.run();
}
}
});
va.setDuration(duration);
va.start();
} else {
setScaleX(finalScale);
setScaleY(finalScale);
mUserImage.setColorFilter(Color.argb(finalFilterAlpha, INACTIVE_COLOR,
INACTIVE_COLOR, INACTIVE_COLOR));
if (onComplete != null) {
post(onComplete);
}
}
}
}
boolean mLockDrawableState = false;
public void lockDrawableState() {
mLockDrawableState = true;
}
public void resetDrawableState() {
mLockDrawableState = false;
post(new Runnable() {
@Override
public void run() {
refreshDrawableState();
}
});
}
protected void drawableStateChanged() {
if (!mLockDrawableState) {
super.drawableStateChanged();
}
}
public UserInfo getUserInfo() {
return mUserInfo;
}
}

View File

@@ -22,15 +22,26 @@ import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserManager;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import android.util.Log;
import android.view.View;
import android.view.WindowManagerGlobal;
import android.widget.FrameLayout;
import com.android.internal.R;
import com.android.internal.policy.impl.keyguard.KeyguardHostView.UserSwitcherCallback;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class KeyguardMultiUserSelectorView extends LinearLayout{
private KeyguardMultiUserAvatar mActiveUser;
private LinearLayout mInactiveUsers;
public class KeyguardMultiUserSelectorView extends FrameLayout implements View.OnClickListener {
private static final String TAG = "KeyguardMultiUserSelectorView";
private KeyguardSubdivisionLayout mUsersGrid;
private KeyguardMultiUserAvatar mActiveUserAvatar;
private UserSwitcherCallback mCallback;
private static final int SWITCH_ANIMATION_DURATION = 150;
private static final int FADE_OUT_ANIMATION_DURATION = 100;
public KeyguardMultiUserSelectorView(Context context) {
this(context, null, 0);
@@ -48,37 +59,77 @@ public class KeyguardMultiUserSelectorView extends LinearLayout{
init();
}
public void setCallback(UserSwitcherCallback callback) {
mCallback = callback;
}
public void init() {
mActiveUser = (KeyguardMultiUserAvatar) findViewById(R.id.keyguard_active_user);
mInactiveUsers = (LinearLayout) findViewById(R.id.keyguard_inactive_users);
mUsersGrid = (KeyguardSubdivisionLayout) findViewById(R.id.keyguard_users_grid);
mUsersGrid.removeAllViews();
setClipChildren(false);
setClipToPadding(false);
mInactiveUsers.removeAllViews();
UserInfo currentUser;
UserInfo activeUser;
try {
currentUser = ActivityManagerNative.getDefault().getCurrentUser();
activeUser = ActivityManagerNative.getDefault().getCurrentUser();
} catch (RemoteException re) {
currentUser = null;
activeUser = null;
}
UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUm.getUsers());
Collections.sort(users, mOrderAddedComparator);
for (UserInfo user: users) {
if (user.id == currentUser.id) {
setActiveUser(user);
} else {
createAndAddInactiveUser(user);
KeyguardMultiUserAvatar uv = createAndAddUser(user);
if (user.id == activeUser.id) {
mActiveUserAvatar = uv;
}
}
mActiveUserAvatar.setActive(true, false, 0, null);
}
private void setActiveUser(UserInfo user) {
mActiveUser.setup(user, this);
}
Comparator<UserInfo> mOrderAddedComparator = new Comparator<UserInfo>() {
@Override
public int compare(UserInfo lhs, UserInfo rhs) {
return (lhs.serialNumber - rhs.serialNumber);
}
};
private void createAndAddInactiveUser(UserInfo user) {
private KeyguardMultiUserAvatar createAndAddUser(UserInfo user) {
KeyguardMultiUserAvatar uv = KeyguardMultiUserAvatar.fromXml(
R.layout.keyguard_multi_user_avatar, mContext, this, user);
mInactiveUsers.addView(uv);
mUsersGrid.addView(uv);
return uv;
}
@Override
public void onClick(View v) {
if (!(v instanceof KeyguardMultiUserAvatar)) return;
final KeyguardMultiUserAvatar avatar = (KeyguardMultiUserAvatar) v;
if (mActiveUserAvatar == avatar) {
// They clicked the active user, no need to do anything
return;
} else {
// Reset the previously active user to appear inactive
avatar.lockDrawableState();
mCallback.hideSecurityView(FADE_OUT_ANIMATION_DURATION);
mActiveUserAvatar.setActive(false, true, SWITCH_ANIMATION_DURATION, new Runnable() {
@Override
public void run() {
try {
ActivityManagerNative.getDefault().switchUser(avatar.getUserInfo().id);
WindowManagerGlobal.getWindowManagerService().lockNow();
// Set the new active user, and make it appear active
avatar.resetDrawableState();
mCallback.showSecurityView();
mActiveUserAvatar = avatar;
mActiveUserAvatar.setActive(true, false, 0, null);
} catch (RemoteException re) {
Log.e(TAG, "Couldn't switch user " + re);
}
}
});
}
}
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright (C) 2012 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.internal.policy.impl.keyguard;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
/**
* A layout that arranges its children into a special type of grid.
*/
public class KeyguardSubdivisionLayout extends ViewGroup {
ArrayList<BiTree> mCells = new ArrayList<BiTree>();
int mNumChildren = -1;
int mWidth = -1;
int mHeight = -1;
int mTopChild = 0;
public KeyguardSubdivisionLayout(Context context) {
this(context, null, 0);
}
public KeyguardSubdivisionLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public KeyguardSubdivisionLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setClipChildren(false);
setClipToPadding(false);
setChildrenDrawingOrderEnabled(true);
}
private class BiTree {
Rect rect;
BiTree left;
BiTree right;
int nodeDepth;
ArrayList<BiTree> leafs;
public BiTree(Rect r) {
rect = r;
}
public BiTree() {
}
boolean isLeaf() {
return (left == null) && (right == null);
}
int depth() {
if (left != null && right != null) {
return Math.max(left.depth(), right.depth()) + 1;
} else if (left != null) {
return left.depth() + 1;
} else if (right != null) {
return right.depth() + 1;
} else {
return 1;
}
}
int numLeafs() {
if (left != null && right != null) {
return left.numLeafs() + right.numLeafs();
} else if (left != null) {
return left.numLeafs();
} else if (right != null) {
return right.numLeafs();
} else {
return 1;
}
}
BiTree getNextNodeToBranch() {
if (leafs == null) {
leafs = new ArrayList<BiTree>();
}
leafs.clear();
getLeafs(leafs, 1);
// We find the first leaf who's depth is not
double r = log2(leafs.size());
if (Math.ceil(r) == Math.floor(r)) {
return leafs.get(leafs.size() - 1);
}
int treeDepth = depth();
for (int i = leafs.size() - 1; i >= 0; i--) {
BiTree n = leafs.get(i);
if (n.nodeDepth < treeDepth) {
return n;
}
}
return null;
}
// Gets leafs in left to right order
void getLeafs(ArrayList<BiTree> leafs, int depth) {
if (isLeaf()) {
this.nodeDepth = depth;
leafs.add(this);
} else {
if (left != null) {
left.getLeafs(leafs, depth + 1);
}
if (right != null) {
right.getLeafs(leafs, depth + 1);
}
}
}
}
double log2(double d) {
return Math.log(d) / Math.log(2);
}
private void addCell(BiTree tree) {
BiTree branch = tree.getNextNodeToBranch();
Rect r = branch.rect;
branch.left = new BiTree();
branch.right = new BiTree();
int newDepth = tree.depth();
// For each level of the tree, we alternate between horizontal and vertical division
if (newDepth % 2 == 0) {
// Divide the cell vertically
branch.left.rect = new Rect(r.left, r.top, r.right, r.top + r.height() / 2);
branch.right.rect = new Rect(r.left, r.top + r.height() / 2, r.right, r.bottom);
} else {
// Divide the cell horizontally
branch.left.rect = new Rect(r.left, r.top, r.left + r.width() / 2, r.bottom);
branch.right.rect = new Rect(r.left + r.width() / 2, r.top, r.right, r.bottom);
}
}
private void constructGrid(int width, int height, int numChildren) {
mCells.clear();
BiTree root = new BiTree(new Rect(0, 0, width, height));
// We add nodes systematically until the number of leafs matches the number of children
while (root.numLeafs() < numChildren) {
addCell(root);
}
// Spit out the final list of cells
root.getLeafs(mCells, 1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int childCount = getChildCount();
if (mNumChildren != childCount || width != getMeasuredWidth() ||
height != getMeasuredHeight()) {
constructGrid(width, height, childCount);
}
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
Rect rect = mCells.get(i).rect;
child.measure(MeasureSpec.makeMeasureSpec(rect.width(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(rect.height(), MeasureSpec.EXACTLY));
}
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
Rect rect = mCells.get(i).rect;
child.layout(rect.left, rect.top, rect.right, rect.bottom);
}
}
public void setTopChild(int top) {
mTopChild = top;
invalidate();
}
protected int getChildDrawingOrder(int childCount, int i) {
int ret = i;
if (i == childCount - 1) {
ret = mTopChild;
} else if (i >= mTopChild){
ret = i + 1;
}
return ret;
}
}