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