summaryrefslogtreecommitdiffstats
path: root/policy
diff options
context:
space:
mode:
authorAdam Cohen <adamcohen@google.com>2012-09-14 15:02:27 -0700
committerAdam Cohen <adamcohen@google.com>2012-09-16 14:46:02 -0700
commit24b351ab748cf1b2e41d106508604ba09f62d859 (patch)
tree0416bd568d569a64c6c6504b0afbb3b6bbe5349b /policy
parentb4ad71aee547d37ff65e67d2fd8743da0b17b258 (diff)
downloadframeworks_base-24b351ab748cf1b2e41d106508604ba09f62d859.zip
frameworks_base-24b351ab748cf1b2e41d106508604ba09f62d859.tar.gz
frameworks_base-24b351ab748cf1b2e41d106508604ba09f62d859.tar.bz2
Second pass on Keyguard multi-user switcher
-> animating active user to inactive, fading out lock selector, freezing screen, then switching users -> for now, just using a fade, but this will likely see iteration Change-Id: I3acc0b53f2cbf132741e3e0529e2e53fa2824542
Diffstat (limited to 'policy')
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java27
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java104
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java91
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java212
4 files changed, 396 insertions, 38 deletions
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 95f8500..400c286 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -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);
}
@@ -690,11 +695,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);
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
index 8aef68f..759068d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
@@ -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;
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
index ba99a55..01d5d8c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
@@ -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 init() {
- mActiveUser = (KeyguardMultiUserAvatar) findViewById(R.id.keyguard_active_user);
- mInactiveUsers = (LinearLayout) findViewById(R.id.keyguard_inactive_users);
+ public void setCallback(UserSwitcherCallback callback) {
+ mCallback = callback;
+ }
- mInactiveUsers.removeAllViews();
+ public void init() {
+ mUsersGrid = (KeyguardSubdivisionLayout) findViewById(R.id.keyguard_users_grid);
+ mUsersGrid.removeAllViews();
+ setClipChildren(false);
+ setClipToPadding(false);
- 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);
+ }
+ }
+ });
+ }
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java
new file mode 100644
index 0000000..b7d94a9
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java
@@ -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;
+ }
+} \ No newline at end of file