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 17package com.android.systemui.statusbar.policy; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.ObjectAnimator; 22import android.content.Context; 23import android.database.DataSetObserver; 24import android.util.AttributeSet; 25import android.view.LayoutInflater; 26import android.view.MotionEvent; 27import android.view.View; 28import android.view.ViewGroup; 29import android.view.ViewStub; 30import android.widget.FrameLayout; 31 32import com.android.settingslib.animation.AppearAnimationUtils; 33import com.android.systemui.Interpolators; 34import com.android.systemui.R; 35import com.android.systemui.qs.tiles.UserDetailItemView; 36import com.android.systemui.statusbar.phone.KeyguardStatusBarView; 37import com.android.systemui.statusbar.phone.NotificationPanelView; 38 39/** 40 * Manages the user switcher on the Keyguard. 41 */ 42public class KeyguardUserSwitcher { 43 44 private static final String TAG = "KeyguardUserSwitcher"; 45 private static final boolean ALWAYS_ON = false; 46 47 private final Container mUserSwitcherContainer; 48 private final KeyguardStatusBarView mStatusBarView; 49 private final Adapter mAdapter; 50 private final AppearAnimationUtils mAppearAnimationUtils; 51 private final KeyguardUserSwitcherScrim mBackground; 52 53 private ViewGroup mUserSwitcher; 54 private ObjectAnimator mBgAnimator; 55 private UserSwitcherController mUserSwitcherController; 56 private boolean mAnimating; 57 58 public KeyguardUserSwitcher(Context context, ViewStub userSwitcher, 59 KeyguardStatusBarView statusBarView, NotificationPanelView panelView, 60 UserSwitcherController userSwitcherController) { 61 boolean keyguardUserSwitcherEnabled = 62 context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON; 63 if (userSwitcherController != null && keyguardUserSwitcherEnabled) { 64 mUserSwitcherContainer = (Container) userSwitcher.inflate(); 65 mBackground = new KeyguardUserSwitcherScrim(context); 66 reinflateViews(); 67 mStatusBarView = statusBarView; 68 mStatusBarView.setKeyguardUserSwitcher(this); 69 panelView.setKeyguardUserSwitcher(this); 70 mAdapter = new Adapter(context, userSwitcherController, this); 71 mAdapter.registerDataSetObserver(mDataSetObserver); 72 mUserSwitcherController = userSwitcherController; 73 mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f, 74 Interpolators.FAST_OUT_SLOW_IN); 75 mUserSwitcherContainer.setKeyguardUserSwitcher(this); 76 } else { 77 mUserSwitcherContainer = null; 78 mStatusBarView = null; 79 mAdapter = null; 80 mAppearAnimationUtils = null; 81 mBackground = null; 82 } 83 } 84 85 private void reinflateViews() { 86 if (mUserSwitcher != null) { 87 mUserSwitcher.setBackground(null); 88 mUserSwitcher.removeOnLayoutChangeListener(mBackground); 89 } 90 mUserSwitcherContainer.removeAllViews(); 91 92 LayoutInflater.from(mUserSwitcherContainer.getContext()) 93 .inflate(R.layout.keyguard_user_switcher_inner, mUserSwitcherContainer); 94 95 mUserSwitcher = (ViewGroup) mUserSwitcherContainer.findViewById( 96 R.id.keyguard_user_switcher_inner); 97 mUserSwitcher.addOnLayoutChangeListener(mBackground); 98 mUserSwitcher.setBackground(mBackground); 99 } 100 101 public void setKeyguard(boolean keyguard, boolean animate) { 102 if (mUserSwitcher != null) { 103 if (keyguard && shouldExpandByDefault()) { 104 show(animate); 105 } else { 106 hide(animate); 107 } 108 } 109 } 110 111 /** 112 * @return true if the user switcher should be expanded by default on the lock screen. 113 * @see android.os.UserManager#isUserSwitcherEnabled() 114 */ 115 private boolean shouldExpandByDefault() { 116 return (mUserSwitcherController != null) && mUserSwitcherController.isSimpleUserSwitcher(); 117 } 118 119 public void show(boolean animate) { 120 if (mUserSwitcher != null && mUserSwitcherContainer.getVisibility() != View.VISIBLE) { 121 cancelAnimations(); 122 mAdapter.refresh(); 123 mUserSwitcherContainer.setVisibility(View.VISIBLE); 124 mStatusBarView.setKeyguardUserSwitcherShowing(true, animate); 125 if (animate) { 126 startAppearAnimation(); 127 } 128 } 129 } 130 131 private void hide(boolean animate) { 132 if (mUserSwitcher != null && mUserSwitcherContainer.getVisibility() == View.VISIBLE) { 133 cancelAnimations(); 134 if (animate) { 135 startDisappearAnimation(); 136 } else { 137 mUserSwitcherContainer.setVisibility(View.GONE); 138 } 139 mStatusBarView.setKeyguardUserSwitcherShowing(false, animate); 140 } 141 } 142 143 private void cancelAnimations() { 144 int count = mUserSwitcher.getChildCount(); 145 for (int i = 0; i < count; i++) { 146 mUserSwitcher.getChildAt(i).animate().cancel(); 147 } 148 if (mBgAnimator != null) { 149 mBgAnimator.cancel(); 150 } 151 mUserSwitcher.animate().cancel(); 152 mAnimating = false; 153 } 154 155 private void startAppearAnimation() { 156 int count = mUserSwitcher.getChildCount(); 157 View[] objects = new View[count]; 158 for (int i = 0; i < count; i++) { 159 objects[i] = mUserSwitcher.getChildAt(i); 160 } 161 mUserSwitcher.setClipChildren(false); 162 mUserSwitcher.setClipToPadding(false); 163 mAppearAnimationUtils.startAnimation(objects, new Runnable() { 164 @Override 165 public void run() { 166 mUserSwitcher.setClipChildren(true); 167 mUserSwitcher.setClipToPadding(true); 168 } 169 }); 170 mAnimating = true; 171 mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255); 172 mBgAnimator.setDuration(400); 173 mBgAnimator.setInterpolator(Interpolators.ALPHA_IN); 174 mBgAnimator.addListener(new AnimatorListenerAdapter() { 175 @Override 176 public void onAnimationEnd(Animator animation) { 177 mBgAnimator = null; 178 mAnimating = false; 179 } 180 }); 181 mBgAnimator.start(); 182 } 183 184 private void startDisappearAnimation() { 185 mAnimating = true; 186 mUserSwitcher.animate() 187 .alpha(0f) 188 .setDuration(300) 189 .setInterpolator(Interpolators.ALPHA_OUT) 190 .withEndAction(new Runnable() { 191 @Override 192 public void run() { 193 mUserSwitcherContainer.setVisibility(View.GONE); 194 mUserSwitcher.setAlpha(1f); 195 mAnimating = false; 196 } 197 }); 198 } 199 200 private void refresh() { 201 final int childCount = mUserSwitcher.getChildCount(); 202 final int adapterCount = mAdapter.getCount(); 203 final int N = Math.max(childCount, adapterCount); 204 for (int i = 0; i < N; i++) { 205 if (i < adapterCount) { 206 View oldView = null; 207 if (i < childCount) { 208 oldView = mUserSwitcher.getChildAt(i); 209 } 210 View newView = mAdapter.getView(i, oldView, mUserSwitcher); 211 if (oldView == null) { 212 // We ran out of existing views. Add it at the end. 213 mUserSwitcher.addView(newView); 214 } else if (oldView != newView) { 215 // We couldn't rebind the view. Replace it. 216 mUserSwitcher.removeViewAt(i); 217 mUserSwitcher.addView(newView, i); 218 } 219 } else { 220 int lastIndex = mUserSwitcher.getChildCount() - 1; 221 mUserSwitcher.removeViewAt(lastIndex); 222 } 223 } 224 } 225 226 public void hideIfNotSimple(boolean animate) { 227 if (mUserSwitcherContainer != null && !mUserSwitcherController.isSimpleUserSwitcher()) { 228 hide(animate); 229 } 230 } 231 232 boolean isAnimating() { 233 return mAnimating; 234 } 235 236 public final DataSetObserver mDataSetObserver = new DataSetObserver() { 237 @Override 238 public void onChanged() { 239 refresh(); 240 } 241 }; 242 243 public void onDensityOrFontScaleChanged() { 244 if (mUserSwitcherContainer != null) { 245 reinflateViews(); 246 refresh(); 247 } 248 } 249 250 public static class Adapter extends UserSwitcherController.BaseUserAdapter implements 251 View.OnClickListener { 252 253 private Context mContext; 254 private KeyguardUserSwitcher mKeyguardUserSwitcher; 255 256 public Adapter(Context context, UserSwitcherController controller, 257 KeyguardUserSwitcher kgu) { 258 super(controller); 259 mContext = context; 260 mKeyguardUserSwitcher = kgu; 261 } 262 263 @Override 264 public View getView(int position, View convertView, ViewGroup parent) { 265 UserSwitcherController.UserRecord item = getItem(position); 266 if (!(convertView instanceof UserDetailItemView) 267 || !(convertView.getTag() instanceof UserSwitcherController.UserRecord)) { 268 convertView = LayoutInflater.from(mContext).inflate( 269 R.layout.keyguard_user_switcher_item, parent, false); 270 convertView.setOnClickListener(this); 271 } 272 UserDetailItemView v = (UserDetailItemView) convertView; 273 274 String name = getName(mContext, item); 275 if (item.picture == null) { 276 v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId()); 277 } else { 278 v.bind(name, item.picture, item.info.id); 279 } 280 // Disable the icon if switching is disabled 281 v.setAvatarEnabled(item.isSwitchToEnabled); 282 convertView.setActivated(item.isCurrent); 283 convertView.setTag(item); 284 return convertView; 285 } 286 287 @Override 288 public void onClick(View v) { 289 UserSwitcherController.UserRecord user = (UserSwitcherController.UserRecord) v.getTag(); 290 if (user.isCurrent && !user.isGuest) { 291 // Close the switcher if tapping the current user. Guest is excluded because 292 // tapping the guest user while it's current clears the session. 293 mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); 294 } else if (user.isSwitchToEnabled) { 295 switchTo(user); 296 } 297 } 298 } 299 300 public static class Container extends FrameLayout { 301 302 private KeyguardUserSwitcher mKeyguardUserSwitcher; 303 304 public Container(Context context, AttributeSet attrs) { 305 super(context, attrs); 306 setClipChildren(false); 307 } 308 309 public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) { 310 mKeyguardUserSwitcher = keyguardUserSwitcher; 311 } 312 313 @Override 314 public boolean onTouchEvent(MotionEvent ev) { 315 // Hide switcher if it didn't handle the touch event (and let the event go through). 316 if (mKeyguardUserSwitcher != null && !mKeyguardUserSwitcher.isAnimating()) { 317 mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); 318 } 319 return false; 320 } 321 } 322} 323