am 62539859: Merge "Second pass on Keyguard multi-user switcher" into jb-mr1-dev
* commit '625398598894b18d797cc407632b78979b20a604': Second pass on Keyguard multi-user switcher
This commit is contained in:
@@ -17,9 +17,6 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
<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"
|
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
|
||||||
android:duration="500"
|
android:duration="500"
|
||||||
android:interpolator="@interpolator/decelerate_quad" />
|
android:interpolator="@interpolator/decelerate_quad" />
|
||||||
|
|||||||
@@ -17,9 +17,6 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
<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"
|
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
|
||||||
android:duration="500"
|
android:duration="500"
|
||||||
android:interpolator="@interpolator/decelerate_quad" />
|
android:interpolator="@interpolator/decelerate_quad" />
|
||||||
|
|||||||
20
core/res/res/drawable/kg_avatar_overlay.xml
Normal file
20
core/res/res/drawable/kg_avatar_overlay.xml
Normal 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>
|
||||||
@@ -23,9 +23,11 @@
|
|||||||
android:layout_width="125dp"
|
android:layout_width="125dp"
|
||||||
android:layout_height="125dp"
|
android:layout_height="125dp"
|
||||||
android:background="#550000ff"
|
android:background="#550000ff"
|
||||||
android:gravity="center_horizontal">
|
android:gravity="center_horizontal"
|
||||||
|
android:foreground="@drawable/kg_avatar_overlay">
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/keyguard_user_avatar"
|
android:id="@+id/keyguard_user_avatar"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"/>
|
android:layout_gravity="center"/>
|
||||||
|
|||||||
@@ -19,23 +19,15 @@
|
|||||||
<com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView
|
<com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="375dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center">
|
android:layout_gravity="center">
|
||||||
|
|
||||||
<include
|
<com.android.internal.policy.impl.keyguard.KeyguardSubdivisionLayout
|
||||||
android:id="@+id/keyguard_active_user"
|
android:id="@+id/keyguard_users_grid"
|
||||||
android:layout_width="250dp"
|
android:orientation="horizontal"
|
||||||
android:layout_height="250dp"
|
android:layout_width="300dp"
|
||||||
layout="@layout/keyguard_multi_user_avatar"/>
|
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>
|
</com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView>
|
||||||
@@ -1260,10 +1260,9 @@
|
|||||||
<java-symbol type="id" name="pin_delete_button" />
|
<java-symbol type="id" name="pin_delete_button" />
|
||||||
<java-symbol type="id" name="keyguard_user_avatar" />
|
<java-symbol type="id" name="keyguard_user_avatar" />
|
||||||
<java-symbol type="id" name="keyguard_user_name" />
|
<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_transport_control" />
|
||||||
<java-symbol type="id" name="keyguard_status_view" />
|
<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_carDockRotation" />
|
||||||
<java-symbol type="integer" name="config_defaultUiModeType" />
|
<java-symbol type="integer" name="config_defaultUiModeType" />
|
||||||
<java-symbol type="integer" name="config_deskDockRotation" />
|
<java-symbol type="integer" name="config_deskDockRotation" />
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ public class KeyguardHostView extends KeyguardViewBase {
|
|||||||
void show();
|
void show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*package*/ interface UserSwitcherCallback {
|
||||||
|
void hideSecurityView(int duration);
|
||||||
|
void showSecurityView();
|
||||||
|
}
|
||||||
|
|
||||||
public KeyguardHostView(Context context) {
|
public KeyguardHostView(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
}
|
}
|
||||||
@@ -691,11 +696,27 @@ public class KeyguardHostView extends KeyguardViewBase {
|
|||||||
List<UserInfo> users = mUm.getUsers();
|
List<UserInfo> users = mUm.getUsers();
|
||||||
|
|
||||||
if (users.size() > 1) {
|
if (users.size() > 1) {
|
||||||
KeyguardWidgetFrame userswitcher = (KeyguardWidgetFrame)
|
KeyguardWidgetFrame userSwitcher = (KeyguardWidgetFrame)
|
||||||
LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
|
LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
|
||||||
mAppWidgetContainer, false);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,16 +16,17 @@
|
|||||||
|
|
||||||
package com.android.internal.policy.impl.keyguard;
|
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.Context;
|
||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowManagerGlobal;
|
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -33,11 +34,15 @@ import android.widget.TextView;
|
|||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
|
|
||||||
class KeyguardMultiUserAvatar extends FrameLayout {
|
class KeyguardMultiUserAvatar extends FrameLayout {
|
||||||
private static final String TAG = "KeyguardViewHost";
|
|
||||||
|
|
||||||
private ImageView mUserImage;
|
private ImageView mUserImage;
|
||||||
private TextView mUserName;
|
private TextView mUserName;
|
||||||
private UserInfo mUserInfo;
|
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;
|
private KeyguardMultiUserSelectorView mUserSelector;
|
||||||
|
|
||||||
public static KeyguardMultiUserAvatar fromXml(int resId, Context context,
|
public static KeyguardMultiUserAvatar fromXml(int resId, Context context,
|
||||||
@@ -73,17 +78,86 @@ class KeyguardMultiUserAvatar extends FrameLayout {
|
|||||||
|
|
||||||
mUserImage.setImageDrawable(Drawable.createFromPath(mUserInfo.iconPath));
|
mUserImage.setImageDrawable(Drawable.createFromPath(mUserInfo.iconPath));
|
||||||
mUserName.setText(mUserInfo.name);
|
mUserName.setText(mUserInfo.name);
|
||||||
setOnClickListener(new OnClickListener() {
|
setOnClickListener(mUserSelector);
|
||||||
@Override
|
setActive(false, false, 0, null);
|
||||||
public void onClick(View v) {
|
mInit = false;
|
||||||
try {
|
}
|
||||||
ActivityManagerNative.getDefault().switchUser(mUserInfo.id);
|
|
||||||
WindowManagerGlobal.getWindowManagerService().lockNow();
|
public void setActive(boolean active, boolean animate, int duration, final Runnable onComplete) {
|
||||||
mUserSelector.init();
|
if (mActive != active || mInit) {
|
||||||
} catch (RemoteException re) {
|
mActive = active;
|
||||||
Log.e(TAG, "Couldn't switch user " + re);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,15 +22,26 @@ import android.content.pm.UserInfo;
|
|||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.util.AttributeSet;
|
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.R;
|
||||||
|
import com.android.internal.policy.impl.keyguard.KeyguardHostView.UserSwitcherCallback;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
public class KeyguardMultiUserSelectorView extends LinearLayout{
|
public class KeyguardMultiUserSelectorView extends FrameLayout implements View.OnClickListener {
|
||||||
private KeyguardMultiUserAvatar mActiveUser;
|
private static final String TAG = "KeyguardMultiUserSelectorView";
|
||||||
private LinearLayout mInactiveUsers;
|
|
||||||
|
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) {
|
public KeyguardMultiUserSelectorView(Context context) {
|
||||||
this(context, null, 0);
|
this(context, null, 0);
|
||||||
@@ -48,37 +59,77 @@ public class KeyguardMultiUserSelectorView extends LinearLayout{
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCallback(UserSwitcherCallback callback) {
|
||||||
|
mCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
mActiveUser = (KeyguardMultiUserAvatar) findViewById(R.id.keyguard_active_user);
|
mUsersGrid = (KeyguardSubdivisionLayout) findViewById(R.id.keyguard_users_grid);
|
||||||
mInactiveUsers = (LinearLayout) findViewById(R.id.keyguard_inactive_users);
|
mUsersGrid.removeAllViews();
|
||||||
|
setClipChildren(false);
|
||||||
|
setClipToPadding(false);
|
||||||
|
|
||||||
mInactiveUsers.removeAllViews();
|
UserInfo activeUser;
|
||||||
|
|
||||||
UserInfo currentUser;
|
|
||||||
try {
|
try {
|
||||||
currentUser = ActivityManagerNative.getDefault().getCurrentUser();
|
activeUser = ActivityManagerNative.getDefault().getCurrentUser();
|
||||||
} catch (RemoteException re) {
|
} catch (RemoteException re) {
|
||||||
currentUser = null;
|
activeUser = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
||||||
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUm.getUsers());
|
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUm.getUsers());
|
||||||
|
Collections.sort(users, mOrderAddedComparator);
|
||||||
|
|
||||||
for (UserInfo user: users) {
|
for (UserInfo user: users) {
|
||||||
if (user.id == currentUser.id) {
|
KeyguardMultiUserAvatar uv = createAndAddUser(user);
|
||||||
setActiveUser(user);
|
if (user.id == activeUser.id) {
|
||||||
} else {
|
mActiveUserAvatar = uv;
|
||||||
createAndAddInactiveUser(user);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mActiveUserAvatar.setActive(true, false, 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setActiveUser(UserInfo user) {
|
Comparator<UserInfo> mOrderAddedComparator = new Comparator<UserInfo>() {
|
||||||
mActiveUser.setup(user, this);
|
@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(
|
KeyguardMultiUserAvatar uv = KeyguardMultiUserAvatar.fromXml(
|
||||||
R.layout.keyguard_multi_user_avatar, mContext, this, user);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user