diff options
author | Adam Cohen <adamcohen@google.com> | 2012-09-14 15:02:27 -0700 |
---|---|---|
committer | Adam Cohen <adamcohen@google.com> | 2012-09-16 14:46:02 -0700 |
commit | 24b351ab748cf1b2e41d106508604ba09f62d859 (patch) | |
tree | 0416bd568d569a64c6c6504b0afbb3b6bbe5349b /policy | |
parent | b4ad71aee547d37ff65e67d2fd8743da0b17b258 (diff) | |
download | frameworks_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')
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 |