/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.systemui.statusbar.policy; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.database.DataSetObserver; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; import android.widget.FrameLayout; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.qs.tiles.UserDetailItemView; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; import com.android.systemui.statusbar.phone.NotificationPanelView; /** * Manages the user switcher on the Keyguard. */ public class KeyguardUserSwitcher { private static final String TAG = "KeyguardUserSwitcher"; private static final boolean ALWAYS_ON = false; private final Container mUserSwitcherContainer; private final KeyguardStatusBarView mStatusBarView; private final Adapter mAdapter; private final AppearAnimationUtils mAppearAnimationUtils; private final KeyguardUserSwitcherScrim mBackground; private ViewGroup mUserSwitcher; private ObjectAnimator mBgAnimator; private UserSwitcherController mUserSwitcherController; private boolean mAnimating; public KeyguardUserSwitcher(Context context, ViewStub userSwitcher, KeyguardStatusBarView statusBarView, NotificationPanelView panelView) { boolean keyguardUserSwitcherEnabled = context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON; UserSwitcherController userSwitcherController = Dependency.get(UserSwitcherController.class); if (userSwitcherController != null && keyguardUserSwitcherEnabled) { mUserSwitcherContainer = (Container) userSwitcher.inflate(); mBackground = new KeyguardUserSwitcherScrim(context); reinflateViews(); mStatusBarView = statusBarView; mStatusBarView.setKeyguardUserSwitcher(this); panelView.setKeyguardUserSwitcher(this); mAdapter = new Adapter(context, userSwitcherController, this); mAdapter.registerDataSetObserver(mDataSetObserver); mUserSwitcherController = userSwitcherController; mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f, Interpolators.FAST_OUT_SLOW_IN); mUserSwitcherContainer.setKeyguardUserSwitcher(this); } else { mUserSwitcherContainer = null; mStatusBarView = null; mAdapter = null; mAppearAnimationUtils = null; mBackground = null; } } private void reinflateViews() { if (mUserSwitcher != null) { mUserSwitcher.setBackground(null); mUserSwitcher.removeOnLayoutChangeListener(mBackground); } mUserSwitcherContainer.removeAllViews(); LayoutInflater.from(mUserSwitcherContainer.getContext()) .inflate(R.layout.keyguard_user_switcher_inner, mUserSwitcherContainer); mUserSwitcher = (ViewGroup) mUserSwitcherContainer.findViewById( R.id.keyguard_user_switcher_inner); mUserSwitcher.addOnLayoutChangeListener(mBackground); mUserSwitcher.setBackground(mBackground); } public void setKeyguard(boolean keyguard, boolean animate) { if (mUserSwitcher != null) { if (keyguard && shouldExpandByDefault()) { show(animate); } else { hide(animate); } } } /** * @return true if the user switcher should be expanded by default on the lock screen. * @see android.os.UserManager#isUserSwitcherEnabled() */ private boolean shouldExpandByDefault() { return (mUserSwitcherController != null) && mUserSwitcherController.isSimpleUserSwitcher(); } public void show(boolean animate) { if (mUserSwitcher != null && mUserSwitcherContainer.getVisibility() != View.VISIBLE) { cancelAnimations(); mAdapter.refresh(); mUserSwitcherContainer.setVisibility(View.VISIBLE); mStatusBarView.setKeyguardUserSwitcherShowing(true, animate); if (animate) { startAppearAnimation(); } } } private boolean hide(boolean animate) { if (mUserSwitcher != null && mUserSwitcherContainer.getVisibility() == View.VISIBLE) { cancelAnimations(); if (animate) { startDisappearAnimation(); } else { mUserSwitcherContainer.setVisibility(View.GONE); } mStatusBarView.setKeyguardUserSwitcherShowing(false, animate); return true; } return false; } private void cancelAnimations() { int count = mUserSwitcher.getChildCount(); for (int i = 0; i < count; i++) { mUserSwitcher.getChildAt(i).animate().cancel(); } if (mBgAnimator != null) { mBgAnimator.cancel(); } mUserSwitcher.animate().cancel(); mAnimating = false; } private void startAppearAnimation() { int count = mUserSwitcher.getChildCount(); View[] objects = new View[count]; for (int i = 0; i < count; i++) { objects[i] = mUserSwitcher.getChildAt(i); } mUserSwitcher.setClipChildren(false); mUserSwitcher.setClipToPadding(false); mAppearAnimationUtils.startAnimation(objects, new Runnable() { @Override public void run() { mUserSwitcher.setClipChildren(true); mUserSwitcher.setClipToPadding(true); } }); mAnimating = true; mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255); mBgAnimator.setDuration(400); mBgAnimator.setInterpolator(Interpolators.ALPHA_IN); mBgAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mBgAnimator = null; mAnimating = false; } }); mBgAnimator.start(); } private void startDisappearAnimation() { mAnimating = true; mUserSwitcher.animate() .alpha(0f) .setDuration(300) .setInterpolator(Interpolators.ALPHA_OUT) .withEndAction(new Runnable() { @Override public void run() { mUserSwitcherContainer.setVisibility(View.GONE); mUserSwitcher.setAlpha(1f); mAnimating = false; } }); } private void refresh() { final int childCount = mUserSwitcher.getChildCount(); final int adapterCount = mAdapter.getCount(); final int N = Math.max(childCount, adapterCount); for (int i = 0; i < N; i++) { if (i < adapterCount) { View oldView = null; if (i < childCount) { oldView = mUserSwitcher.getChildAt(i); } View newView = mAdapter.getView(i, oldView, mUserSwitcher); if (oldView == null) { // We ran out of existing views. Add it at the end. mUserSwitcher.addView(newView); } else if (oldView != newView) { // We couldn't rebind the view. Replace it. mUserSwitcher.removeViewAt(i); mUserSwitcher.addView(newView, i); } } else { int lastIndex = mUserSwitcher.getChildCount() - 1; mUserSwitcher.removeViewAt(lastIndex); } } } public boolean hideIfNotSimple(boolean animate) { if (mUserSwitcherContainer != null && !mUserSwitcherController.isSimpleUserSwitcher()) { return hide(animate); } return false; } boolean isAnimating() { return mAnimating; } public final DataSetObserver mDataSetObserver = new DataSetObserver() { @Override public void onChanged() { refresh(); } }; public void onDensityOrFontScaleChanged() { if (mUserSwitcherContainer != null) { reinflateViews(); refresh(); } } public static class Adapter extends UserSwitcherController.BaseUserAdapter implements View.OnClickListener { private Context mContext; private KeyguardUserSwitcher mKeyguardUserSwitcher; public Adapter(Context context, UserSwitcherController controller, KeyguardUserSwitcher kgu) { super(controller); mContext = context; mKeyguardUserSwitcher = kgu; } @Override public View getView(int position, View convertView, ViewGroup parent) { UserSwitcherController.UserRecord item = getItem(position); if (!(convertView instanceof UserDetailItemView) || !(convertView.getTag() instanceof UserSwitcherController.UserRecord)) { convertView = LayoutInflater.from(mContext).inflate( R.layout.keyguard_user_switcher_item, parent, false); convertView.setOnClickListener(this); } UserDetailItemView v = (UserDetailItemView) convertView; String name = getName(mContext, item); if (item.picture == null) { v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId()); } else { v.bind(name, item.picture, item.info.id); } // Disable the icon if switching is disabled v.setAvatarEnabled(item.isSwitchToEnabled); convertView.setActivated(item.isCurrent); convertView.setTag(item); return convertView; } @Override public void onClick(View v) { UserSwitcherController.UserRecord user = (UserSwitcherController.UserRecord) v.getTag(); if (user.isCurrent && !user.isGuest) { // Close the switcher if tapping the current user. Guest is excluded because // tapping the guest user while it's current clears the session. mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); } else if (user.isSwitchToEnabled) { switchTo(user); } } } public static class Container extends FrameLayout { private KeyguardUserSwitcher mKeyguardUserSwitcher; public Container(Context context, AttributeSet attrs) { super(context, attrs); setClipChildren(false); } public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) { mKeyguardUserSwitcher = keyguardUserSwitcher; } @Override public boolean onTouchEvent(MotionEvent ev) { // Hide switcher if it didn't handle the touch event (and let the event go through). if (mKeyguardUserSwitcher != null && !mKeyguardUserSwitcher.isAnimating()) { mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); } return false; } } }