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