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