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