1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.statusbar.policy; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ObjectAnimator; 22 import android.animation.ValueAnimator; 23 import android.content.Context; 24 import android.database.DataSetObserver; 25 import android.view.LayoutInflater; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.view.ViewStub; 29 import android.view.animation.AnimationUtils; 30 import android.widget.TextView; 31 32 import com.android.keyguard.AppearAnimationUtils; 33 import com.android.systemui.R; 34 import com.android.systemui.qs.tiles.UserDetailItemView; 35 import com.android.systemui.statusbar.phone.KeyguardStatusBarView; 36 import com.android.systemui.statusbar.phone.NotificationPanelView; 37 import com.android.systemui.statusbar.phone.PhoneStatusBar; 38 import com.android.systemui.statusbar.phone.StatusBarHeaderView; 39 import com.android.systemui.statusbar.phone.UserAvatarView; 40 41 /** 42 * Manages the user switcher on the Keyguard. 43 */ 44 public class KeyguardUserSwitcher { 45 46 private static final String TAG = "KeyguardUserSwitcher"; 47 private static final boolean ALWAYS_ON = false; 48 49 private final ViewGroup mUserSwitcher; 50 private final KeyguardStatusBarView mStatusBarView; 51 private final Adapter mAdapter; 52 private final AppearAnimationUtils mAppearAnimationUtils; 53 private final KeyguardUserSwitcherScrim mBackground; 54 private ObjectAnimator mBgAnimator; 55 private UserSwitcherController mUserSwitcherController; 56 57 public KeyguardUserSwitcher(Context context, ViewStub userSwitcher, 58 KeyguardStatusBarView statusBarView, NotificationPanelView panelView, 59 UserSwitcherController userSwitcherController) { 60 if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON) { 61 mUserSwitcher = (ViewGroup) userSwitcher.inflate(); 62 mBackground = new KeyguardUserSwitcherScrim(mUserSwitcher); 63 mUserSwitcher.setBackground(mBackground); 64 mStatusBarView = statusBarView; 65 mStatusBarView.setKeyguardUserSwitcher(this); 66 panelView.setKeyguardUserSwitcher(this); 67 mAdapter = new Adapter(context, userSwitcherController); 68 mAdapter.registerDataSetObserver(mDataSetObserver); 69 mUserSwitcherController = userSwitcherController; 70 mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f, 71 AnimationUtils.loadInterpolator( 72 context, android.R.interpolator.fast_out_slow_in)); 73 } else { 74 mUserSwitcher = null; 75 mStatusBarView = null; 76 mAdapter = null; 77 mAppearAnimationUtils = null; 78 mBackground = null; 79 } 80 } 81 82 public void setKeyguard(boolean keyguard, boolean animate) { 83 if (mUserSwitcher != null) { 84 if (keyguard && shouldExpandByDefault()) { 85 show(animate); 86 } else { 87 hide(animate); 88 } 89 } 90 } 91 92 /** 93 * @return true if the user switcher should be expanded by default on the lock screen. 94 * @see android.os.UserManager#isUserSwitcherEnabled() 95 */ 96 private boolean shouldExpandByDefault() { 97 return (mUserSwitcherController != null) && mUserSwitcherController.isSimpleUserSwitcher(); 98 } 99 100 public void show(boolean animate) { 101 if (mUserSwitcher != null && mUserSwitcher.getVisibility() != View.VISIBLE) { 102 cancelAnimations(); 103 mUserSwitcher.setVisibility(View.VISIBLE); 104 mStatusBarView.setKeyguardUserSwitcherShowing(true, animate); 105 if (animate) { 106 startAppearAnimation(); 107 } 108 } 109 } 110 111 public void hide(boolean animate) { 112 if (mUserSwitcher != null && mUserSwitcher.getVisibility() == View.VISIBLE) { 113 cancelAnimations(); 114 if (animate) { 115 startDisappearAnimation(); 116 } else { 117 mUserSwitcher.setVisibility(View.GONE); 118 } 119 mStatusBarView.setKeyguardUserSwitcherShowing(false, animate); 120 } 121 } 122 123 private void cancelAnimations() { 124 int count = mUserSwitcher.getChildCount(); 125 for (int i = 0; i < count; i++) { 126 mUserSwitcher.getChildAt(i).animate().cancel(); 127 } 128 if (mBgAnimator != null) { 129 mBgAnimator.cancel(); 130 } 131 mUserSwitcher.animate().cancel(); 132 } 133 134 private void startAppearAnimation() { 135 int count = mUserSwitcher.getChildCount(); 136 View[] objects = new View[count]; 137 for (int i = 0; i < count; i++) { 138 objects[i] = mUserSwitcher.getChildAt(i); 139 } 140 mUserSwitcher.setClipChildren(false); 141 mUserSwitcher.setClipToPadding(false); 142 mAppearAnimationUtils.startAppearAnimation(objects, new Runnable() { 143 @Override 144 public void run() { 145 mUserSwitcher.setClipChildren(true); 146 mUserSwitcher.setClipToPadding(true); 147 } 148 }); 149 mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255); 150 mBgAnimator.setDuration(400); 151 mBgAnimator.setInterpolator(PhoneStatusBar.ALPHA_IN); 152 mBgAnimator.addListener(new AnimatorListenerAdapter() { 153 @Override 154 public void onAnimationEnd(Animator animation) { 155 mBgAnimator = null; 156 } 157 }); 158 mBgAnimator.start(); 159 } 160 161 private void startDisappearAnimation() { 162 mUserSwitcher.animate() 163 .alpha(0f) 164 .setDuration(300) 165 .setInterpolator(PhoneStatusBar.ALPHA_OUT) 166 .withEndAction(new Runnable() { 167 @Override 168 public void run() { 169 mUserSwitcher.setVisibility(View.GONE); 170 mUserSwitcher.setAlpha(1f); 171 } 172 }); 173 } 174 175 private void refresh() { 176 final int childCount = mUserSwitcher.getChildCount(); 177 final int adapterCount = mAdapter.getCount(); 178 final int N = Math.max(childCount, adapterCount); 179 for (int i = 0; i < N; i++) { 180 if (i < adapterCount) { 181 View oldView = null; 182 if (i < childCount) { 183 oldView = mUserSwitcher.getChildAt(i); 184 } 185 View newView = mAdapter.getView(i, oldView, mUserSwitcher); 186 if (oldView == null) { 187 // We ran out of existing views. Add it at the end. 188 mUserSwitcher.addView(newView); 189 } else if (oldView != newView) { 190 // We couldn't rebind the view. Replace it. 191 mUserSwitcher.removeViewAt(i); 192 mUserSwitcher.addView(newView, i); 193 } 194 } else { 195 int lastIndex = mUserSwitcher.getChildCount() - 1; 196 mUserSwitcher.removeViewAt(lastIndex); 197 } 198 } 199 } 200 201 public final DataSetObserver mDataSetObserver = new DataSetObserver() { 202 @Override 203 public void onChanged() { 204 refresh(); 205 } 206 }; 207 208 public static class Adapter extends UserSwitcherController.BaseUserAdapter implements 209 View.OnClickListener { 210 211 private Context mContext; 212 213 public Adapter(Context context, UserSwitcherController controller) { 214 super(controller); 215 mContext = context; 216 } 217 218 @Override 219 public View getView(int position, View convertView, ViewGroup parent) { 220 UserSwitcherController.UserRecord item = getItem(position); 221 222 if (!(convertView instanceof UserDetailItemView) 223 || !(convertView.getTag() instanceof UserSwitcherController.UserRecord)) { 224 convertView = LayoutInflater.from(mContext).inflate( 225 R.layout.keyguard_user_switcher_item, parent, false); 226 convertView.setOnClickListener(this); 227 } 228 UserDetailItemView v = (UserDetailItemView) convertView; 229 230 String name = getName(mContext, item); 231 if (item.picture == null) { 232 v.bind(name, getDrawable(mContext, item)); 233 } else { 234 v.bind(name, item.picture); 235 } 236 convertView.setActivated(item.isCurrent); 237 convertView.setTag(item); 238 return convertView; 239 } 240 241 @Override 242 public void onClick(View v) { 243 switchTo(((UserSwitcherController.UserRecord)v.getTag())); 244 } 245 } 246 } 247