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.Dependency;
34import com.android.systemui.Interpolators;
35import com.android.systemui.R;
36import com.android.systemui.qs.tiles.UserDetailItemView;
37import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
38import com.android.systemui.statusbar.phone.NotificationPanelView;
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 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
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
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
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     */
116    private boolean shouldExpandByDefault() {
117        return (mUserSwitcherController != null) && mUserSwitcherController.isSimpleUserSwitcher();
118    }
119
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
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
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
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
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
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
229    public boolean hideIfNotSimple(boolean animate) {
230        if (mUserSwitcherContainer != null && !mUserSwitcherController.isSimpleUserSwitcher()) {
231            return hide(animate);
232        }
233        return false;
234    }
235
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
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
260        public Adapter(Context context, UserSwitcherController controller,
261                KeyguardUserSwitcher kgu) {
262            super(controller);
263            mContext = context;
264            mKeyguardUserSwitcher = kgu;
265        }
266
267        @Override
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
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
308        public Container(Context context, AttributeSet attrs) {
309            super(context, attrs);
310            setClipChildren(false);
311        }
312
313        public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
314            mKeyguardUserSwitcher = keyguardUserSwitcher;
315        }
316
317        @Override
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