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