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
17package com.android.systemui.statusbar.policy;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
22import android.content.Context;
23import android.database.DataSetObserver;
24import android.util.AttributeSet;
25import android.view.LayoutInflater;
26import android.view.MotionEvent;
27import android.view.View;
28import android.view.ViewGroup;
29import android.view.ViewStub;
30import android.view.animation.AnimationUtils;
31import android.widget.FrameLayout;
32
33import com.android.keyguard.AppearAnimationUtils;
34import com.android.systemui.R;
35import com.android.systemui.qs.tiles.UserDetailItemView;
36import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
37import com.android.systemui.statusbar.phone.NotificationPanelView;
38import com.android.systemui.statusbar.phone.PhoneStatusBar;
39
40/**
41 * Manages the user switcher on the Keyguard.
42 */
43public 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
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
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     */
103    private boolean shouldExpandByDefault() {
104        return (mUserSwitcherController != null) && mUserSwitcherController.isSimpleUserSwitcher();
105    }
106
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
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
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
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
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
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
214    public void hideIfNotSimple(boolean animate) {
215        if (mUserSwitcherContainer != null && !mUserSwitcherController.isSimpleUserSwitcher()) {
216            hide(animate);
217        }
218    }
219
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
237        public Adapter(Context context, UserSwitcherController controller,
238                KeyguardUserSwitcher kgu) {
239            super(controller);
240            mContext = context;
241            mKeyguardUserSwitcher = kgu;
242        }
243
244        @Override
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
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
284        public Container(Context context, AttributeSet attrs) {
285            super(context, attrs);
286            setClipChildren(false);
287        }
288
289        public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
290            mKeyguardUserSwitcher = keyguardUserSwitcher;
291        }
292
293        @Override
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