diff options
10 files changed, 428 insertions, 63 deletions
diff --git a/core/res/res/anim/screen_user_enter.xml b/core/res/res/anim/screen_user_enter.xml index a73dea3..cb0a634 100644 --- a/core/res/res/anim/screen_user_enter.xml +++ b/core/res/res/anim/screen_user_enter.xml @@ -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" /> diff --git a/core/res/res/anim/screen_user_exit.xml b/core/res/res/anim/screen_user_exit.xml index ec94b76..3d465be 100644 --- a/core/res/res/anim/screen_user_exit.xml +++ b/core/res/res/anim/screen_user_exit.xml @@ -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" /> diff --git a/core/res/res/drawable/kg_avatar_overlay.xml b/core/res/res/drawable/kg_avatar_overlay.xml new file mode 100644 index 0000000..781c1df --- /dev/null +++ b/core/res/res/drawable/kg_avatar_overlay.xml @@ -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>
\ No newline at end of file diff --git a/core/res/res/layout/keyguard_multi_user_avatar.xml b/core/res/res/layout/keyguard_multi_user_avatar.xml index 9999177..23f9b6d 100644 --- a/core/res/res/layout/keyguard_multi_user_avatar.xml +++ b/core/res/res/layout/keyguard_multi_user_avatar.xml @@ -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"/> diff --git a/core/res/res/layout/keyguard_multi_user_selector.xml b/core/res/res/layout/keyguard_multi_user_selector.xml index 3ed9103..c599fd5 100644 --- a/core/res/res/layout/keyguard_multi_user_selector.xml +++ b/core/res/res/layout/keyguard_multi_user_selector.xml @@ -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>
\ No newline at end of file diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 505f3a4..821bb6b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1334,10 +1334,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" /> 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 |