Home | History | Annotate | Download | only in policy
      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 
     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