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