From aac8e536ab2411a0febacbcfaae1fe79d85f556c Mon Sep 17 00:00:00 2001 From: Beverly Date: Wed, 3 Feb 2021 09:58:26 -0500 Subject: [PATCH] Rotate keyguard indication messages In the new LS layout, we show the following messages in the keyguard bottom area instead of in the KeyguardStatusView: - Logout button - Owner information We also now show now playing as part of the rotating text on the lock screen. Bug: 178794517 Test: atest SystemUITest, manual Change-Id: I1a0a47e300d1f9e5fe11c17d143e5a8f0ad8af60 --- .../res/layout/keyguard_bottom_area.xml | 9 +- packages/SystemUI/res/values/dimens.xml | 5 +- .../android/keyguard/KeyguardStatusView.java | 28 +- .../KeyguardStatusViewController.java | 6 +- .../systemui/keyguard/KeyguardIndication.java | 155 ++++++ ...ardIndicationRotateTextViewController.java | 326 ++++++++++++ .../KeyguardIndicationController.java | 476 +++++++++++++----- .../phone/KeyguardBottomAreaView.java | 7 +- .../phone/KeyguardIndicationTextView.java | 150 ++++-- .../systemui/statusbar/phone/StatusBar.java | 11 +- ...ndicationRotateTextViewControllerTest.java | 304 +++++++++++ .../KeyguardIndicationControllerTest.java | 186 ++++--- .../phone/KeyguardIndicationTextViewTest.java | 6 +- 13 files changed, 1394 insertions(+), 275 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 04de9784812f3..ba6a14762bf64 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -43,14 +43,17 @@ android:accessibilityLiveRegion="polite"/> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index e5109307c4f4c..a639ae04f8b68 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -199,6 +199,9 @@ 32dp + + 24dp + 12sp @@ -1153,7 +1156,7 @@ 32dp 16dp 12dp - 2dp + 4dp 1px diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 97aa26fb7f68d..fea152abe36ae 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -56,8 +56,10 @@ public class KeyguardStatusView extends GridLayout { private final IActivityManager mIActivityManager; private TextView mLogoutView; + private boolean mCanShowLogout = true; // by default, try to show the logout button here private KeyguardClockSwitch mClockView; private TextView mOwnerInfo; + private boolean mCanShowOwnerInfo = true; // by default, try to show the owner information here private KeyguardSliceView mKeyguardSlice; private View mNotificationIcons; private Runnable mPendingMarqueeStart; @@ -114,6 +116,25 @@ public class KeyguardStatusView extends GridLayout { if (mOwnerInfo != null) mOwnerInfo.setSelected(enabled); } + void setCanShowOwnerInfo(boolean canShowOwnerInfo) { + mCanShowOwnerInfo = canShowOwnerInfo; + mOwnerInfo = findViewById(R.id.owner_info); + if (mOwnerInfo != null) { + if (mCanShowOwnerInfo) { + mOwnerInfo.setVisibility(VISIBLE); + updateOwnerInfo(); + } else { + mOwnerInfo.setVisibility(GONE); + mOwnerInfo = null; + } + } + } + + void setCanShowLogout(boolean canShowLogout) { + mCanShowLogout = canShowLogout; + updateLogoutView(); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -128,7 +149,10 @@ public class KeyguardStatusView extends GridLayout { if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) { mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext)); } - mOwnerInfo = findViewById(R.id.owner_info); + if (mCanShowOwnerInfo) { + mOwnerInfo = findViewById(R.id.owner_info); + } + mKeyguardSlice = findViewById(R.id.keyguard_status_area); mTextColor = mClockView.getCurrentTextColor(); @@ -189,7 +213,7 @@ public class KeyguardStatusView extends GridLayout { if (mLogoutView == null) { return; } - mLogoutView.setVisibility(shouldShowLogout() ? VISIBLE : GONE); + mLogoutView.setVisibility(mCanShowLogout && shouldShowLogout() ? VISIBLE : GONE); // Logout button will stay in language of user 0 if we don't set that manually. mLogoutView.setText(mContext.getResources().getString( com.android.internal.R.string.global_action_logout)); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 973b49384c09d..a5f364d30d7de 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -134,7 +134,7 @@ public class KeyguardStatusViewController extends ViewController implements Dumpable { + public static String TAG = "KgIndicationRotatingCtrl"; + private static final long DEFAULT_INDICATION_SHOW_LENGTH = 3500; // milliseconds + + private final StatusBarStateController mStatusBarStateController; + private final float mMaxAlpha; + private final ColorStateList mInitialTextColorState; + + // Stores @IndicationType => KeyguardIndication messages + private final Map mIndicationMessages = new HashMap<>(); + + // Executor that will show the next message after a delay + private final DelayableExecutor mExecutor; + @Nullable private ShowNextIndication mShowNextIndicationRunnable; + + // List of indication types to show. The next indication to show is always at index 0 + private final List mIndicationQueue = new LinkedList<>(); + private @IndicationType int mCurrIndicationType = INDICATION_TYPE_NONE; + + private boolean mIsDozing; + + public KeyguardIndicationRotateTextViewController( + KeyguardIndicationTextView view, + @Main DelayableExecutor executor, + StatusBarStateController statusBarStateController, + int lockScreenMode + ) { + super(view); + mMaxAlpha = view.getAlpha(); + mExecutor = executor; + mInitialTextColorState = mView != null + ? mView.getTextColors() : ColorStateList.valueOf(Color.WHITE); + mStatusBarStateController = statusBarStateController; + mView.setLockScreenMode(lockScreenMode); + init(); + } + + @Override + protected void onViewAttached() { + mStatusBarStateController.addCallback(mStatusBarStateListener); + } + + @Override + protected void onViewDetached() { + mStatusBarStateController.removeCallback(mStatusBarStateListener); + cancelScheduledIndication(); + } + + /** + * Update the indication type with the given String. + * @param type of indication + * @param newIndication message to associate with this indication type + * @param showImmediately if true: shows this indication message immediately. Else, the text + * associated with this type is updated and will show when its turn in + * the IndicationQueue comes around. + */ + public void updateIndication(@IndicationType int type, KeyguardIndication newIndication, + boolean showImmediately) { + final boolean hasPreviousIndication = mIndicationMessages.get(type) != null; + final boolean hasNewIndication = newIndication != null + && !TextUtils.isEmpty(newIndication.getMessage()); + if (!hasNewIndication) { + mIndicationMessages.remove(type); + mIndicationQueue.removeIf(x -> x == type); + } else { + if (!hasPreviousIndication) { + mIndicationQueue.add(type); + } + + mIndicationMessages.put(type, newIndication); + } + + if (mIsDozing) { + return; + } + + final boolean showNow = showImmediately + || mCurrIndicationType == INDICATION_TYPE_NONE + || mCurrIndicationType == type; + if (hasNewIndication) { + if (showNow) { + showIndication(type); + } else if (!isNextIndicationScheduled()) { + scheduleShowNextIndication(); + } + return; + } + + if (mCurrIndicationType == type + && !hasNewIndication + && showImmediately) { + if (mShowNextIndicationRunnable != null) { + mShowNextIndicationRunnable.runImmediately(); + } else { + showIndication(INDICATION_TYPE_NONE); + } + } + } + + /** + * Stop showing the following indication type. + * + * If the current indication is of this type, immediately stops showing the message. + */ + public void hideIndication(@IndicationType int type) { + updateIndication(type, null, true); + } + + /** + * Show a transient message. + * Transient messages: + * - show immediately + * - will continue to be in the rotation of messages shown until hideTransient is called. + * - can be presented with an "error" color if isError is true + */ + public void showTransient(CharSequence newIndication, boolean isError) { + updateIndication(INDICATION_TYPE_TRANSIENT, + new KeyguardIndication.Builder() + .setMessage(newIndication) + .setTextColor(isError + ? Utils.getColorError(getContext()) + : mInitialTextColorState) + .build(), + /* showImmediately */true); + } + + /** + * Hide a transient message immediately. + */ + public void hideTransient() { + hideIndication(INDICATION_TYPE_TRANSIENT); + } + + /** + * @return true if there are available indications to show + */ + public boolean hasIndications() { + return mIndicationMessages.keySet().size() > 0; + } + + /** + * Immediately show the passed indication type and schedule the next indication to show. + * Will re-add this indication to be re-shown after all other indications have been + * rotated through. + */ + private void showIndication(@IndicationType int type) { + cancelScheduledIndication(); + + mCurrIndicationType = type; + mIndicationQueue.removeIf(x -> x == type); + if (mCurrIndicationType == INDICATION_TYPE_NONE) { + mView.setVisibility(View.GONE); + } else { + mView.setVisibility(View.VISIBLE); + mIndicationQueue.add(type); // re-add to show later + } + + // pass the style update to be run right before our new indication is shown: + mView.switchIndication(mIndicationMessages.get(type)); + + // only schedule next indication if there's more than just this indication in the queue + if (mCurrIndicationType != INDICATION_TYPE_NONE && mIndicationQueue.size() > 1) { + scheduleShowNextIndication(); + } + } + + protected boolean isNextIndicationScheduled() { + return mShowNextIndicationRunnable != null; + } + + private void scheduleShowNextIndication() { + cancelScheduledIndication(); + mShowNextIndicationRunnable = new ShowNextIndication(DEFAULT_INDICATION_SHOW_LENGTH); + } + + private void cancelScheduledIndication() { + if (mShowNextIndicationRunnable != null) { + mShowNextIndicationRunnable.cancelDelayedExecution(); + mShowNextIndicationRunnable = null; + } + } + + private StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onDozeAmountChanged(float linear, float eased) { + mView.setAlpha((1 - linear) * mMaxAlpha); + } + + @Override + public void onDozingChanged(boolean isDozing) { + if (isDozing == mIsDozing) return; + mIsDozing = isDozing; + if (mIsDozing) { + showIndication(INDICATION_TYPE_NONE); + } else if (mIndicationQueue.size() > 0) { + showIndication(mIndicationQueue.remove(0)); + } + } + }; + + /** + * Shows the next indication in the IndicationQueue after an optional delay. + * This wrapper has the ability to cancel itself (remove runnable from DelayableExecutor) or + * immediately run itself (which also removes itself from the DelayableExecutor). + */ + class ShowNextIndication { + private final Runnable mShowIndicationRunnable; + private Runnable mCancelDelayedRunnable; + + ShowNextIndication(long delay) { + mShowIndicationRunnable = () -> { + int type = mIndicationQueue.size() == 0 + ? INDICATION_TYPE_NONE : mIndicationQueue.remove(0); + showIndication(type); + }; + mCancelDelayedRunnable = mExecutor.executeDelayed(mShowIndicationRunnable, delay); + } + + public void runImmediately() { + cancelDelayedExecution(); + mShowIndicationRunnable.run(); + } + + public void cancelDelayedExecution() { + if (mCancelDelayedRunnable != null) { + mCancelDelayedRunnable.run(); + mCancelDelayedRunnable = null; + } + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("KeyguardIndicationRotatingTextViewController:"); + pw.println(" currentMessage=" + mView.getText()); + pw.println(" dozing:" + mIsDozing); + pw.println(" queue:" + mIndicationQueue.toString()); + pw.println(" showNextIndicationRunnable:" + mShowNextIndicationRunnable); + + if (hasIndications()) { + pw.println(" All messages:"); + for (int type : mIndicationMessages.keySet()) { + pw.println(" type=" + type + " message=" + mIndicationMessages.get(type)); + } + } + } + + private static final int INDICATION_TYPE_NONE = -1; + public static final int INDICATION_TYPE_OWNER_INFO = 0; + public static final int INDICATION_TYPE_DISCLOSURE = 1; + public static final int INDICATION_TYPE_LOGOUT = 2; + public static final int INDICATION_TYPE_BATTERY = 3; + public static final int INDICATION_TYPE_ALIGNMENT = 4; + public static final int INDICATION_TYPE_TRANSIENT = 5; + public static final int INDICATION_TYPE_TRUST = 6; + public static final int INDICATION_TYPE_RESTING = 7; + public static final int INDICATION_TYPE_USER_LOCKED = 8; + public static final int INDICATION_TYPE_NOW_PLAYING = 9; + public static final int INDICATION_TYPE_REVERSE_CHARGING = 10; + + @IntDef({ + INDICATION_TYPE_NONE, + INDICATION_TYPE_DISCLOSURE, + INDICATION_TYPE_OWNER_INFO, + INDICATION_TYPE_LOGOUT, + INDICATION_TYPE_BATTERY, + INDICATION_TYPE_ALIGNMENT, + INDICATION_TYPE_TRANSIENT, + INDICATION_TYPE_TRUST, + INDICATION_TYPE_RESTING, + INDICATION_TYPE_USER_LOCKED, + INDICATION_TYPE_NOW_PLAYING, + INDICATION_TYPE_REVERSE_CHARGING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface IndicationType{} +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index c816784a22f23..c70a93b5c8947 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -16,10 +16,24 @@ package com.android.systemui.statusbar; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1; import static com.android.systemui.DejankUtils.whitelistIpcs; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.app.ActivityManager; +import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -47,6 +61,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; +import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.ViewClippingUtil; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; @@ -56,13 +71,16 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; +import com.android.systemui.keyguard.KeyguardIndication; +import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.wakelock.SettableWakeLock; import com.android.systemui.util.wakelock.WakeLock; @@ -76,8 +94,7 @@ import javax.inject.Inject; * Controls the indications and error messages shown on the Keyguard */ @SysUISingleton -public class KeyguardIndicationController implements StateListener, - KeyguardStateController.Callback { +public class KeyguardIndicationController implements KeyguardStateController.Callback { private static final String TAG = "KeyguardIndication"; private static final boolean DEBUG_CHARGING_SPEED = false; @@ -94,14 +111,17 @@ public class KeyguardIndicationController implements StateListener, private final StatusBarStateController mStatusBarStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private ViewGroup mIndicationArea; - private KeyguardIndicationTextView mTextView; - private KeyguardIndicationTextView mDisclosure; + private KeyguardIndicationTextView mTopIndicationView; private final IBatteryStats mBatteryInfo; private final SettableWakeLock mWakeLock; private final DockManager mDockManager; private final DevicePolicyManager mDevicePolicyManager; private final UserManager mUserManager; + private final @Main DelayableExecutor mExecutor; + private final LockPatternUtils mLockPatternUtils; + private final IActivityManager mIActivityManager; + protected KeyguardIndicationRotateTextViewController mRotateTextViewController; private BroadcastReceiver mBroadcastReceiver; private LockscreenLockIconController mLockIconController; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -110,7 +130,7 @@ public class KeyguardIndicationController implements StateListener, private String mAlignmentIndication; private CharSequence mTransientIndication; private boolean mTransientTextIsError; - private ColorStateList mInitialTextColorState; + protected ColorStateList mInitialTextColorState; private boolean mVisible; private boolean mHideTransientMessageOnScreenOff; @@ -124,8 +144,8 @@ public class KeyguardIndicationController implements StateListener, private int mBatteryLevel; private boolean mBatteryPresent = true; private long mChargingTimeRemaining; - private float mDisclosureMaxAlpha; private String mMessageToShowOnScreenOn; + protected int mLockScreenMode; private KeyguardUpdateMonitorCallback mUpdateMonitorCallback; @@ -151,7 +171,8 @@ public class KeyguardIndicationController implements StateListener, BroadcastDispatcher broadcastDispatcher, DevicePolicyManager devicePolicyManager, IBatteryStats iBatteryStats, - UserManager userManager) { + UserManager userManager, + @Main DelayableExecutor executor) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; mDevicePolicyManager = devicePolicyManager; @@ -165,23 +186,29 @@ public class KeyguardIndicationController implements StateListener, wakeLockBuilder.setTag("Doze:KeyguardIndication").build(), TAG); mBatteryInfo = iBatteryStats; mUserManager = userManager; + mExecutor = executor; + mLockPatternUtils = new LockPatternUtils(context); + mIActivityManager = ActivityManager.getService(); mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback()); mKeyguardUpdateMonitor.registerCallback(mTickReceiver); - mStatusBarStateController.addCallback(this); + mStatusBarStateController.addCallback(mStatusBarStateListener); mKeyguardStateController.addCallback(this); } public void setIndicationArea(ViewGroup indicationArea) { mIndicationArea = indicationArea; - mTextView = indicationArea.findViewById(R.id.keyguard_indication_text); - mInitialTextColorState = mTextView != null ? - mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE); - mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure); - mDisclosureMaxAlpha = mDisclosure.getAlpha(); + mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text); + mInitialTextColorState = mTopIndicationView != null + ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE); + mRotateTextViewController = new KeyguardIndicationRotateTextViewController( + indicationArea.findViewById(R.id.keyguard_indication_text_bottom), + mExecutor, + mStatusBarStateController, + mLockScreenMode); updateIndication(false /* animate */); updateDisclosure(); - + updateOwnerInfo(); if (mBroadcastReceiver == null) { // Update the disclosure proactively to avoid IPC on the critical path. mBroadcastReceiver = new BroadcastReceiver() { @@ -233,19 +260,196 @@ public class KeyguardIndicationController implements StateListener, return mUpdateMonitorCallback; } + /** + * Doesn't include owner information or disclosure which get triggered separately. + */ + private void updateIndications(boolean animate, int userId) { + updateBattery(animate); + updateUserLocked(userId); + updateTransient(); + updateTrust(userId, getTrustGrantedIndication(), getTrustManagedIndication()); + updateAlignment(); + updateResting(); + } + private void updateDisclosure() { - // NOTE: Because this uses IPC, avoid calling updateDisclosure() on a critical path. if (whitelistIpcs(this::isOrganizationOwnedDevice)) { - CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName(); - if (organizationName != null) { - mDisclosure.switchIndication(mContext.getResources().getString( - R.string.do_disclosure_with_name, organizationName)); - } else { - mDisclosure.switchIndication(R.string.do_disclosure_generic); - } - mDisclosure.setVisibility(View.VISIBLE); + final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName(); + final CharSequence disclosure = organizationName != null + ? mContext.getResources().getString(R.string.do_disclosure_with_name, + organizationName) + : mContext.getResources().getText(R.string.do_disclosure_generic); + mRotateTextViewController.updateIndication( + INDICATION_TYPE_DISCLOSURE, + new KeyguardIndication.Builder() + .setMessage(disclosure) + .setTextColor(mInitialTextColorState) + .build(), + /* updateImmediately */ false); } else { - mDisclosure.setVisibility(View.GONE); + mRotateTextViewController.hideIndication(INDICATION_TYPE_DISCLOSURE); + } + + if (isKeyguardLayoutEnabled()) { + updateIndication(false); // resting indication may need to update + } + } + + private void updateBattery(boolean animate) { + if (mPowerPluggedIn || mEnableBatteryDefender) { + String powerIndication = computePowerIndication(); + if (DEBUG_CHARGING_SPEED) { + powerIndication += ", " + (mChargingWattage / 1000) + " mW"; + } + + mRotateTextViewController.updateIndication( + INDICATION_TYPE_BATTERY, + new KeyguardIndication.Builder() + .setMessage(powerIndication) + .setTextColor(mInitialTextColorState) + .build(), + animate); + } else { + // don't show the charging information if device isn't plugged in + mRotateTextViewController.hideIndication(INDICATION_TYPE_BATTERY); + } + } + + private void updateUserLocked(int userId) { + if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_USER_LOCKED, + new KeyguardIndication.Builder() + .setMessage(mContext.getResources().getText( + com.android.internal.R.string.lockscreen_storage_locked)) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_USER_LOCKED); + } + } + + private void updateTransient() { + if (!TextUtils.isEmpty(mTransientIndication)) { + mRotateTextViewController.showTransient(mTransientIndication, + mTransientTextIsError); + } else { + mRotateTextViewController.hideTransient(); + } + } + + private void updateTrust(int userId, CharSequence trustGrantedIndication, + CharSequence trustManagedIndication) { + if (!TextUtils.isEmpty(trustGrantedIndication) + && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_TRUST, + new KeyguardIndication.Builder() + .setMessage(trustGrantedIndication) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else if (!TextUtils.isEmpty(trustManagedIndication) + && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) + && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_TRUST, + new KeyguardIndication.Builder() + .setMessage(trustManagedIndication) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_TRUST); + } + } + + private void updateAlignment() { + if (!TextUtils.isEmpty(mAlignmentIndication)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_ALIGNMENT, + new KeyguardIndication.Builder() + .setMessage(mAlignmentIndication) + .setTextColor(Utils.getColorError(mContext)) + .build(), + true); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_ALIGNMENT); + } + } + + private void updateResting() { + if (mRestingIndication != null + && !mRotateTextViewController.hasIndications()) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_RESTING, + new KeyguardIndication.Builder() + .setMessage(mRestingIndication) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_RESTING); + } + } + + protected boolean isKeyguardLayoutEnabled() { + return mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1; + } + + private void updateLogoutView() { + if (!isKeyguardLayoutEnabled()) { + return; + } + final boolean shouldShowLogout = mKeyguardUpdateMonitor.isLogoutEnabled() + && KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM; + String logoutString = shouldShowLogout ? mContext.getResources().getString( + com.android.internal.R.string.global_action_logout) : null; + mRotateTextViewController.updateIndication( + INDICATION_TYPE_LOGOUT, + new KeyguardIndication.Builder() + .setMessage(logoutString) + .setTextColor(mInitialTextColorState) + .setBackground(mContext.getDrawable( + com.android.systemui.R.drawable.logout_button_background)) + .setClickListener((view) -> { + int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); + try { + mIActivityManager.switchUser(UserHandle.USER_SYSTEM); + mIActivityManager.stopUser(currentUserId, true /* force */, null); + } catch (RemoteException re) { + Log.e(TAG, "Failed to logout user", re); + } + }) + .build(), + false); + updateIndication(false); // resting indication may need to update + } + + private void updateOwnerInfo() { + if (!isKeyguardLayoutEnabled()) { + return; + } + String info = mLockPatternUtils.getDeviceOwnerInfo(); + if (info == null) { + // Use the current user owner information if enabled. + final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled( + KeyguardUpdateMonitor.getCurrentUser()); + if (ownerInfoEnabled) { + info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser()); + } + } + if (info != null) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_OWNER_INFO, + new KeyguardIndication.Builder() + .setMessage(info) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + updateIndication(false); // resting indication may need to update } } @@ -281,9 +485,10 @@ public class KeyguardIndicationController implements StateListener, return UserHandle.USER_NULL; } - public void setVisible(boolean visible) { + @VisibleForTesting + protected void setVisible(boolean visible) { mVisible = visible; - mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE); + mIndicationArea.setVisibility(visible ? VISIBLE : GONE); if (visible) { // If this is called after an error message was already shown, we should not clear it. // Otherwise the error message won't be shown @@ -382,6 +587,7 @@ public class KeyguardIndicationController implements StateListener, mTransientIndication = null; mHideTransientMessageOnScreenOff = false; mHandler.removeMessages(MSG_HIDE_TRANSIENT); + mRotateTextViewController.hideTransient(); updateIndication(false); } } @@ -396,98 +602,109 @@ public class KeyguardIndicationController implements StateListener, } // A few places might need to hide the indication, so always start by making it visible - mIndicationArea.setVisibility(View.VISIBLE); + mIndicationArea.setVisibility(VISIBLE); // Walk down a precedence-ordered list of what indication // should be shown based on user or device state + // AoD if (mDozing) { + mTopIndicationView.setVisibility(VISIBLE); // When dozing we ignore any text color and use white instead, because // colors can be hard to read in low brightness. - mTextView.setTextColor(Color.WHITE); + mTopIndicationView.setTextColor(Color.WHITE); if (!TextUtils.isEmpty(mTransientIndication)) { - mTextView.switchIndication(mTransientIndication); + mTopIndicationView.switchIndication(mTransientIndication, null); } else if (!mBatteryPresent) { // If there is no battery detected, hide the indication and bail - mIndicationArea.setVisibility(View.GONE); + mIndicationArea.setVisibility(GONE); } else if (!TextUtils.isEmpty(mAlignmentIndication)) { - mTextView.switchIndication(mAlignmentIndication); - mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); + mTopIndicationView.switchIndication(mAlignmentIndication, null); + mTopIndicationView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); } else if (mPowerPluggedIn || mEnableBatteryDefender) { String indication = computePowerIndication(); if (animate) { - animateText(mTextView, indication); + animateText(mTopIndicationView, indication); } else { - mTextView.switchIndication(indication); + mTopIndicationView.switchIndication(indication, null); } } else { String percentage = NumberFormat.getPercentInstance() .format(mBatteryLevel / 100f); - mTextView.switchIndication(percentage); + mTopIndicationView.switchIndication(percentage, null); } return; } - int userId = KeyguardUpdateMonitor.getCurrentUser(); - String trustGrantedIndication = getTrustGrantedIndication(); - String trustManagedIndication = getTrustManagedIndication(); - - String powerIndication = null; - if (mPowerPluggedIn || mEnableBatteryDefender) { - powerIndication = computePowerIndication(); - } - + // LOCK SCREEN // Some cases here might need to hide the indication (if the battery is not present) - boolean hideIndication = false; - boolean isError = false; - if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { - mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked); - } else if (!TextUtils.isEmpty(mTransientIndication)) { - if (powerIndication != null && !mTransientIndication.equals(powerIndication)) { - String indication = mContext.getResources().getString( - R.string.keyguard_indication_trust_unlocked_plugged_in, - mTransientIndication, powerIndication); - mTextView.switchIndication(indication); - hideIndication = !mBatteryPresent; - } else { - mTextView.switchIndication(mTransientIndication); - } - isError = mTransientTextIsError; - } else if (!TextUtils.isEmpty(trustGrantedIndication) - && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { - if (powerIndication != null) { - String indication = mContext.getResources().getString( - R.string.keyguard_indication_trust_unlocked_plugged_in, - trustGrantedIndication, powerIndication); - mTextView.switchIndication(indication); - hideIndication = !mBatteryPresent; - } else { - mTextView.switchIndication(trustGrantedIndication); - } - } else if (!TextUtils.isEmpty(mAlignmentIndication)) { - mTextView.switchIndication(mAlignmentIndication); - isError = true; - hideIndication = !mBatteryPresent; - } else if (mPowerPluggedIn || mEnableBatteryDefender) { - if (DEBUG_CHARGING_SPEED) { - powerIndication += ", " + (mChargingWattage / 1000) + " mW"; - } - if (animate) { - animateText(mTextView, powerIndication); - } else { - mTextView.switchIndication(powerIndication); - } - hideIndication = !mBatteryPresent; - } else if (!TextUtils.isEmpty(trustManagedIndication) - && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) - && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { - mTextView.switchIndication(trustManagedIndication); + int userId = KeyguardUpdateMonitor.getCurrentUser(); + + if (mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1) { + mTopIndicationView.setVisibility(GONE); + updateIndications(animate, userId); } else { - mTextView.switchIndication(mRestingIndication); - } - mTextView.setTextColor(isError ? Utils.getColorError(mContext) - : mInitialTextColorState); - if (hideIndication) { - mIndicationArea.setVisibility(View.GONE); + boolean hideIndication = false; + boolean isError = false; + String trustGrantedIndication = getTrustGrantedIndication(); + String trustManagedIndication = getTrustManagedIndication(); + String powerIndication = null; + + if (mPowerPluggedIn || mEnableBatteryDefender) { + powerIndication = computePowerIndication(); + } + if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { + mTopIndicationView.switchIndication( + com.android.internal.R.string.lockscreen_storage_locked); + } else if (!TextUtils.isEmpty(mTransientIndication)) { + if (powerIndication != null && !mTransientIndication.equals(powerIndication)) { + String indication = mContext.getResources().getString( + R.string.keyguard_indication_trust_unlocked_plugged_in, + mTransientIndication, powerIndication); + mTopIndicationView.switchIndication(indication, null); + hideIndication = !mBatteryPresent; + } else { + mTopIndicationView.switchIndication(mTransientIndication, null); + } + isError = mTransientTextIsError; + } else if (!TextUtils.isEmpty(trustGrantedIndication) + && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + if (powerIndication != null) { + String indication = mContext.getResources().getString( + R.string.keyguard_indication_trust_unlocked_plugged_in, + trustGrantedIndication, powerIndication); + mTopIndicationView.switchIndication(indication, null); + hideIndication = !mBatteryPresent; + } else { + mTopIndicationView.switchIndication(trustGrantedIndication, null); + } + } else if (!TextUtils.isEmpty(mAlignmentIndication)) { + mTopIndicationView.switchIndication(mAlignmentIndication, null); + isError = true; + hideIndication = !mBatteryPresent; + } else if (mPowerPluggedIn || mEnableBatteryDefender) { + if (DEBUG_CHARGING_SPEED) { + powerIndication += ", " + (mChargingWattage / 1000) + " mW"; + } + if (animate) { + animateText(mTopIndicationView, powerIndication); + } else { + mTopIndicationView.switchIndication(powerIndication, null); + } + hideIndication = !mBatteryPresent; + } else if (!TextUtils.isEmpty(trustManagedIndication) + && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) + && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + mTopIndicationView.switchIndication(trustManagedIndication, null); + } else { + mTopIndicationView.switchIndication(mRestingIndication, null); + } + + mTopIndicationView.setTextColor( + isError ? Utils.getColorError(mContext) : mInitialTextColorState); + + if (hideIndication) { + mIndicationArea.setVisibility(GONE); + } } } @@ -510,7 +727,7 @@ public class KeyguardIndicationController implements StateListener, @Override public void onAnimationStart(Animator animation) { - textView.switchIndication(indication); + textView.switchIndication(indication, null); } @Override @@ -634,18 +851,6 @@ public class KeyguardIndicationController implements StateListener, } } - public void setDozing(boolean dozing) { - if (mDozing == dozing) { - return; - } - mDozing = dozing; - if (mHideTransientMessageOnScreenOff && mDozing) { - hideTransientIndication(); - } else { - updateIndication(false); - } - } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("KeyguardIndicationController:"); pw.println(" mTransientTextIsError: " + mTransientTextIsError); @@ -659,23 +864,10 @@ public class KeyguardIndicationController implements StateListener, pw.println(" mDozing: " + mDozing); pw.println(" mBatteryLevel: " + mBatteryLevel); pw.println(" mBatteryPresent: " + mBatteryPresent); - pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText())); + pw.println(" mTextView.getText(): " + ( + mTopIndicationView == null ? null : mTopIndicationView.getText())); pw.println(" computePowerIndication(): " + computePowerIndication()); - } - - @Override - public void onStateChanged(int newState) { - // don't care - } - - @Override - public void onDozingChanged(boolean isDozing) { - setDozing(isDozing); - } - - @Override - public void onDozeAmountChanged(float linear, float eased) { - mDisclosure.setAlpha((1 - linear) * mDisclosureMaxAlpha); + mRotateTextViewController.dump(fd, pw, args); } @Override @@ -686,6 +878,11 @@ public class KeyguardIndicationController implements StateListener, protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback { public static final int HIDE_DELAY_MS = 5000; + @Override + public void onLockScreenModeChanged(int mode) { + mLockScreenMode = mode; + } + @Override public void onRefreshBatteryInfo(BatteryStatus status) { boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING @@ -845,6 +1042,7 @@ public class KeyguardIndicationController implements StateListener, @Override public void onUserSwitchComplete(int userId) { if (mVisible) { + updateOwnerInfo(); updateIndication(false); } } @@ -856,12 +1054,40 @@ public class KeyguardIndicationController implements StateListener, } } + @Override + public void onLogoutEnabledChanged() { + if (mVisible) { + updateLogoutView(); + } + } + @Override public void onRequireUnlockForNfc() { showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc), false /* isError */, false /* hideOnScreenOff */); hideTransientIndicationDelayed(HIDE_DELAY_MS); } - } + + private StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + setVisible(newState == StatusBarState.KEYGUARD); + } + + @Override + public void onDozingChanged(boolean dozing) { + if (mDozing == dozing) { + return; + } + mDozing = dozing; + + if (mHideTransientMessageOnScreenOff && mDozing) { + hideTransientIndication(); + } else { + updateIndication(false); + } + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 0a366c9bb3804..3ff04d2d83a1b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -123,8 +123,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private KeyguardAffordanceView mRightAffordanceView; private KeyguardAffordanceView mLeftAffordanceView; private ViewGroup mIndicationArea; - private TextView mEnterpriseDisclosure; private TextView mIndicationText; + private TextView mIndicationTextBottom; private ViewGroup mPreviewContainer; private ViewGroup mOverlayContainer; @@ -237,9 +237,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mRightAffordanceView = findViewById(R.id.camera_button); mLeftAffordanceView = findViewById(R.id.left_button); mIndicationArea = findViewById(R.id.keyguard_indication_area); - mEnterpriseDisclosure = findViewById( - R.id.keyguard_indication_enterprise_disclosure); mIndicationText = findViewById(R.id.keyguard_indication_text); + mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom); mIndicationBottomMargin = getResources().getDimensionPixelSize( R.dimen.keyguard_indication_margin_bottom); mBurnInYOffset = getResources().getDimensionPixelSize( @@ -316,7 +315,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } // Respect font size setting. - mEnterpriseDisclosure.setTextSize(TypedValue.COMPLEX_UNIT_PX, + mIndicationTextBottom.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize( com.android.internal.R.dimen.text_size_small_material)); mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java index 30c951ac518cc..8970a9a20931c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java @@ -16,11 +16,15 @@ package com.android.systemui.statusbar.phone; +import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; @@ -28,6 +32,7 @@ import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Interpolators; +import com.android.systemui.keyguard.KeyguardIndication; import java.util.LinkedList; @@ -35,13 +40,15 @@ import java.util.LinkedList; * A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open"). */ public class KeyguardIndicationTextView extends TextView { - private static final int FADE_OUT_MILLIS = 200; private static final int FADE_IN_MILLIS = 250; private static final long MSG_DURATION_MILLIS = 600; private long mNextAnimationTime = 0; private boolean mAnimationsEnabled = true; private LinkedList mMessages = new LinkedList<>(); + private LinkedList mKeyguardIndicationInfo = new LinkedList<>(); + + private boolean mUseNewAnimations = false; public KeyguardIndicationTextView(Context context) { super(context); @@ -60,12 +67,33 @@ public class KeyguardIndicationTextView extends TextView { super(context, attrs, defStyleAttr, defStyleRes); } + public void setLockScreenMode(int lockScreenMode) { + mUseNewAnimations = lockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1; + } + + /** + * Changes the text with an animation and makes sure a single indication is shown long enough. + */ + public void switchIndication(int textResId) { + switchIndication(getResources().getText(textResId), null); + } + + /** + * Changes the text with an animation and makes sure a single indication is shown long enough. + * + * @param indication The text to show. + */ + public void switchIndication(KeyguardIndication indication) { + switchIndication(indication == null ? null : indication.getMessage(), indication); + } + /** * Changes the text with an animation and makes sure a single indication is shown long enough. * * @param text The text to show. + * @param indication optional display information for the text */ - public void switchIndication(CharSequence text) { + public void switchIndication(CharSequence text, KeyguardIndication indication) { if (text == null) text = ""; CharSequence lastPendingMessage = mMessages.peekLast(); @@ -74,55 +102,119 @@ public class KeyguardIndicationTextView extends TextView { return; } mMessages.add(text); + mKeyguardIndicationInfo.add(indication); - Animator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f); - fadeOut.setDuration(getFadeOutMillis()); - fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); - - final CharSequence nextText = text; - fadeOut.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animator) { - setText(mMessages.poll()); - } - }); - + final boolean hasIcon = indication != null && indication.getIcon() != null; final AnimatorSet animSet = new AnimatorSet(); - final AnimatorSet.Builder animSetBuilder = animSet.play(fadeOut); + final AnimatorSet.Builder animSetBuilder = animSet.play(getOutAnimator()); // Make sure each animation is visible for a minimum amount of time, while not worrying // about fading in blank text long timeInMillis = System.currentTimeMillis(); long delay = Math.max(0, mNextAnimationTime - timeInMillis); - setNextAnimationTime(timeInMillis + delay + getFadeOutMillis()); + setNextAnimationTime(timeInMillis + delay + getFadeOutDuration()); - if (!text.equals("")) { + if (!text.equals("") || hasIcon) { setNextAnimationTime(mNextAnimationTime + MSG_DURATION_MILLIS); - - ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f); - fadeIn.setDuration(getFadeInMillis()); - fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - animSetBuilder.before(fadeIn); + animSetBuilder.before(getInAnimator()); } animSet.setStartDelay(delay); animSet.start(); } + private AnimatorSet getOutAnimator() { + AnimatorSet animatorSet = new AnimatorSet(); + Animator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f); + fadeOut.setDuration(getFadeOutDuration()); + fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + fadeOut.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + KeyguardIndication info = mKeyguardIndicationInfo.poll(); + if (info != null) { + setTextColor(info.getTextColor()); + setOnClickListener(info.getClickListener()); + final Drawable icon = info.getIcon(); + if (icon != null) { + icon.setTint(getCurrentTextColor()); + if (icon instanceof AnimatedVectorDrawable) { + ((AnimatedVectorDrawable) icon).start(); + } + } + setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null); + } + setText(mMessages.poll()); + } + }); + + if (mUseNewAnimations) { + Animator yTranslate = + ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0, -getYTranslationPixels()); + yTranslate.setDuration(getFadeOutDuration()); + fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + animatorSet.playTogether(fadeOut, yTranslate); + } else { + animatorSet.play(fadeOut); + } + + return animatorSet; + } + + private AnimatorSet getInAnimator() { + AnimatorSet animatorSet = new AnimatorSet(); + ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f); + fadeIn.setStartDelay(getFadeInDelay()); + fadeIn.setDuration(getFadeInDuration()); + fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + + if (mUseNewAnimations) { + Animator yTranslate = + ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, getYTranslationPixels(), 0); + yTranslate.setDuration(getYInDuration()); + yTranslate.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + setTranslationY(0); + } + }); + animatorSet.playTogether(yTranslate, fadeIn); + } else { + animatorSet.play(fadeIn); + } + + return animatorSet; + } + @VisibleForTesting public void setAnimationsEnabled(boolean enabled) { mAnimationsEnabled = enabled; } - private long getFadeInMillis() { - if (mAnimationsEnabled) return FADE_IN_MILLIS; + private long getFadeInDelay() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 150L; return 0L; } - private long getFadeOutMillis() { - if (mAnimationsEnabled) return FADE_OUT_MILLIS; + private long getFadeInDuration() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 317L; + return FADE_IN_MILLIS; + } + + private long getYInDuration() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 600L; return 0L; } + private long getFadeOutDuration() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 167L; + return FADE_OUT_MILLIS; + } + private void setNextAnimationTime(long time) { if (mAnimationsEnabled) { mNextAnimationTime = time; @@ -131,10 +223,8 @@ public class KeyguardIndicationTextView extends TextView { } } - /** - * See {@link #switchIndication}. - */ - public void switchIndication(int textResId) { - switchIndication(getResources().getText(textResId)); + private int getYTranslationPixels() { + return mContext.getResources().getDimensionPixelSize( + com.android.systemui.R.dimen.keyguard_indication_y_translation); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 83651398be433..1425dabc6d9e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1136,8 +1136,6 @@ public class StatusBar extends SystemUI implements DemoMode, mLockscreenWallpaper = mLockscreenWallpaperLazy.get(); } - mKeyguardIndicationController.setIndicationArea( - mNotificationShadeWindowView.findViewById(R.id.keyguard_indication_area)); mNotificationPanelViewController.setKeyguardIndicationController( mKeyguardIndicationController); @@ -3613,26 +3611,19 @@ public class StatusBar extends SystemUI implements DemoMode, mNavigationBarController.touchAutoDim(mDisplayId); Trace.beginSection("StatusBar#updateKeyguardState"); if (mState == StatusBarState.KEYGUARD) { - mKeyguardIndicationController.setVisible(true); if (mKeyguardUserSwitcher != null) { mKeyguardUserSwitcher.setKeyguard(true, mStatusBarStateController.fromShadeLocked()); } if (mStatusBarView != null) mStatusBarView.removePendingHideExpandedRunnables(); - if (mAmbientIndicationContainer != null) { - mAmbientIndicationContainer.setVisibility(View.VISIBLE); - } } else { - mKeyguardIndicationController.setVisible(false); if (mKeyguardUserSwitcher != null) { mKeyguardUserSwitcher.setKeyguard(false, mStatusBarStateController.goingToFullShade() || mState == StatusBarState.SHADE_LOCKED || mStatusBarStateController.fromShadeLocked()); } - if (mAmbientIndicationContainer != null) { - mAmbientIndicationContainer.setVisibility(View.INVISIBLE); - } + } updateDozingState(); checkBarModes(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java new file mode 100644 index 0000000000000..b6729adeb9b3c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2021 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.keyguard; + + +import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +@SmallTest +public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCase { + + private static final String TEST_MESSAGE = "test message"; + private static final String TEST_MESSAGE_2 = "test message 2"; + + @Mock + private DelayableExecutor mExecutor; + @Mock + private KeyguardIndicationTextView mView; + @Mock + private StatusBarStateController mStatusBarStateController; + @Captor + private ArgumentCaptor mStatusBarStateListenerCaptor; + + private KeyguardIndicationRotateTextViewController mController; + private StatusBarStateController.StateListener mStatusBarStateListener; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mView.getTextColors()).thenReturn(ColorStateList.valueOf(Color.WHITE)); + mController = new KeyguardIndicationRotateTextViewController(mView, mExecutor, + mStatusBarStateController, LOCK_SCREEN_MODE_LAYOUT_1); + mController.onViewAttached(); + + verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); + mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue(); + } + + @Test + public void testInitialState_noIndication() { + assertFalse(mController.hasIndications()); + } + + @Test + public void testShowOneIndication() { + // WHEN we add our first indication + final KeyguardIndication indication = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication, false); + + // THEN + // - we see controller has an indication + // - the indication shows immediately since it's the only one + // - no next indication is scheduled since there's only one indication + assertTrue(mController.hasIndications()); + verify(mView).switchIndication(indication); + verify(mExecutor, never()).executeDelayed(any(), anyLong()); + } + + @Test + public void testShowTwoRotatingMessages() { + // GIVEN we already have an indication message + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, createIndication(), false); + reset(mView); + + // WHEN we have a new indication type to display + final KeyguardIndication indication2 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication2, false); + + // THEN + // - we don't immediately see the new message until the delay + // - next indication is scheduled + verify(mView, never()).switchIndication(indication2); + verify(mExecutor).executeDelayed(any(), anyLong()); + } + + @Test + public void testUpdateCurrentMessage() { + // GIVEN we already have an indication message + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, createIndication(), false); + reset(mView); + + // WHEN we have a new message for this indication type to display + final KeyguardIndication indication2 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication2, false); + + // THEN + // - new indication is updated immediately + // - we don't schedule to show anything later + verify(mView).switchIndication(indication2); + verify(mExecutor, never()).executeDelayed(any(), anyLong()); + } + + @Test + public void testUpdateRotatingMessageForUndisplayedIndication() { + // GIVEN we already have two indication messages + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, createIndication(), false); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, createIndication(), false); + reset(mView); + reset(mExecutor); + + // WHEN we have a new message for an undisplayed indication type + final KeyguardIndication indication3 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication3, false); + + // THEN + // - we don't immediately update + // - we don't schedule to show anything new + verify(mView, never()).switchIndication(indication3); + verify(mExecutor, never()).executeDelayed(any(), anyLong()); + } + + @Test + public void testUpdateImmediately() { + // GIVEN we already have three indication messages + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, createIndication(), false); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, createIndication(), false); + mController.updateIndication( + INDICATION_TYPE_BATTERY, createIndication(), false); + reset(mView); + reset(mExecutor); + + // WHEN we have a new message for a currently shown type that we want to show immediately + final KeyguardIndication indication4 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_BATTERY, indication4, true); + + // THEN + // - we immediately update + // - we schedule a new delayable to show the next message later + verify(mView).switchIndication(indication4); + verify(mExecutor).executeDelayed(any(), anyLong()); + + // WHEN an already existing type is updated to show immediately + reset(mView); + reset(mExecutor); + final KeyguardIndication indication5 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication5, true); + + // THEN + // - we immediately update + // - we schedule a new delayable to show the next message later + verify(mView).switchIndication(indication5); + verify(mExecutor).executeDelayed(any(), anyLong()); + } + + @Test + public void testTransientIndication() { + // GIVEN we already have two indication messages + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, createIndication(), false); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, createIndication(), false); + reset(mView); + reset(mExecutor); + + // WHEN we have a transient message + mController.showTransient(TEST_MESSAGE_2, false); + + // THEN + // - we immediately update + // - we schedule a new delayable to show the next message later + verify(mView).switchIndication(any(KeyguardIndication.class)); + verify(mExecutor).executeDelayed(any(), anyLong()); + } + + @Test + public void testHideIndicationOneMessage() { + // GIVEN we have one indication message + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, createIndication(), false); + + // WHEN we hide the current indication type + mController.hideIndication(INDICATION_TYPE_OWNER_INFO); + + // THEN we immediately update the text to show no text + verify(mView).switchIndication(null); + } + + @Test + public void testHideIndicationTwoMessages() { + // GIVEN we have two indication messages + final KeyguardIndication indication1 = createIndication(); + final KeyguardIndication indication2 = createIndication(); + mController.updateIndication( + INDICATION_TYPE_OWNER_INFO, indication1, false); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication2, false); + assertTrue(mController.isNextIndicationScheduled()); + + // WHEN we hide the current indication type + mController.hideIndication(INDICATION_TYPE_OWNER_INFO); + + // THEN we show the next indication and there's no scheduled next indication + verify(mView).switchIndication(indication2); + assertFalse(mController.isNextIndicationScheduled()); + } + + @Test + public void testStartDozing() { + // WHEN the device is dozing + mStatusBarStateListener.onDozingChanged(true); + + // THEN the view is GONE + verify(mView).setVisibility(View.GONE); + } + + @Test + public void testStoppedDozing() { + // GIVEN we're dozing & we have an indication message + mStatusBarStateListener.onDozingChanged(true); + final KeyguardIndication indication = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication, false); + reset(mView); + reset(mExecutor); + + // WHEN the device is no longer dozing + mStatusBarStateListener.onDozingChanged(false); + + // THEN show the next message + verify(mView).switchIndication(indication); + } + + @Test + public void testIsDozing() { + // GIVEN the device is dozing + mStatusBarStateListener.onDozingChanged(true); + reset(mView); + + // WHEN an indication is updated + final KeyguardIndication indication = createIndication(); + mController.updateIndication( + INDICATION_TYPE_DISCLOSURE, indication, false); + + // THEN no message is shown since we're dozing + verify(mView, never()).switchIndication(any()); + } + + private KeyguardIndication createIndication() { + return new KeyguardIndication.Builder() + .setMessage(TEST_MESSAGE) + .setTextColor(ColorStateList.valueOf(Color.WHITE)) + .build(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 2c96535b52bdd..0cae67427e3b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; @@ -27,13 +29,11 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.Instrumentation; @@ -51,7 +51,6 @@ import android.os.BatteryManager; import android.os.Looper; import android.os.RemoteException; import android.os.UserManager; -import android.view.View; import android.view.ViewGroup; import androidx.test.InstrumentationRegistry; @@ -60,18 +59,21 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.app.IBatteryStats; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; +import com.android.systemui.keyguard.KeyguardIndication; +import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import com.android.systemui.statusbar.phone.LockIcon; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.wakelock.WakeLockFake; import org.junit.After; @@ -93,6 +95,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private static final String ORGANIZATION_NAME = "organization"; private String mDisclosureWithOrganization; + private String mDisclosureGeneric; @Mock private DevicePolicyManager mDevicePolicyManager; @@ -101,7 +104,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { @Mock private KeyguardStateController mKeyguardStateController; @Mock - private KeyguardIndicationTextView mDisclosure; + private KeyguardIndicationTextView mIndicationAreaBottom; @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock @@ -118,8 +121,20 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private IBatteryStats mIBatteryStats; @Mock private DockManager mDockManager; + @Mock + private KeyguardIndicationRotateTextViewController mRotateTextViewController; @Captor private ArgumentCaptor mAlignmentListener; + @Captor + private ArgumentCaptor mStatusBarStateListenerCaptor; + @Captor + private ArgumentCaptor mBroadcastReceiverCaptor; + @Captor + private ArgumentCaptor mKeyguardIndicationCaptor; + private StatusBarStateController.StateListener mStatusBarStateListener; + private BroadcastReceiver mBroadcastReceiver; + private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + private KeyguardIndicationTextView mTextView; private KeyguardIndicationController mController; @@ -140,15 +155,15 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class)); mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name, ORGANIZATION_NAME); + mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic); when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true); when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true); - when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure)) - .thenReturn(mDisclosure); + when(mIndicationArea.findViewById(R.id.keyguard_indication_text_bottom)) + .thenReturn(mIndicationAreaBottom); when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView); - when(mDisclosure.getAlpha()).thenReturn(1f); mWakeLock = new WakeLockFake(); mWakeLockBuilder = new WakeLockFake.Builder(mContext); @@ -168,11 +183,15 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mController = new KeyguardIndicationController(mContext, mWakeLockBuilder, mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor, mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats, - mUserManager); + mUserManager, mExecutor); mController.setIndicationArea(mIndicationArea); + verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); + mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue(); + verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(), any()); + mBroadcastReceiver = mBroadcastReceiverCaptor.getValue(); + mController.mRotateTextViewController = mRotateTextViewController; mController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); clearInvocations(mIBatteryStats); - verify(mDisclosure).getAlpha(); } @Test @@ -223,7 +242,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture()); mController.setVisible(true); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_POOR); }); @@ -241,7 +260,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture()); mController.setVisible(true); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE); }); @@ -255,136 +274,108 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { @Test public void disclosure_unmanaged() { + createController(); when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false); when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(false); - createController(); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.GONE); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_DISCLOSURE); + verify(mRotateTextViewController, never()).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + any(), anyBoolean()); } @Test - public void disclosure_deviceOwner_noOwnerName() { + public void disclosure_deviceOwner_noOrganizationName() { + createController(); when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null); - createController(); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureGeneric); } @Test - public void disclosure_orgOwnedDeviceWithManagedProfile_noOwnerName() { + public void disclosure_orgOwnedDeviceWithManagedProfile_noOrganizationName() { + createController(); when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(true); when(mUserManager.getProfiles(anyInt())).thenReturn(Collections.singletonList( new UserInfo(10, /* name */ null, /* flags */ FLAG_MANAGED_PROFILE))); when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(null); - createController(); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureGeneric); } @Test - public void disclosure_hiddenWhenDozing() { - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); - when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null); + public void disclosure_deviceOwner_withOrganizationName() { createController(); - - mController.setVisible(true); - mController.onDozeAmountChanged(1, 1); - mController.setDozing(true); - - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).setAlpha(0f); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); - } - - @Test - public void disclosure_visibleWhenDozing() { - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); - when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null); - createController(); - - mController.setVisible(true); - mController.onDozeAmountChanged(0, 0); - mController.setDozing(false); - - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).setAlpha(1f); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); - } - - @Test - public void disclosure_deviceOwner_withOwnerName() { when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME); - createController(); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(mDisclosureWithOrganization); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureWithOrganization); } @Test - public void disclosure_orgOwnedDeviceWithManagedProfile_withOwnerName() { + public void disclosure_orgOwnedDeviceWithManagedProfile_withOrganizationName() { + createController(); when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(true); when(mUserManager.getProfiles(anyInt())).thenReturn(Collections.singletonList( new UserInfo(10, /* name */ null, FLAG_MANAGED_PROFILE))); when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(ORGANIZATION_NAME); - createController(); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(mDisclosureWithOrganization); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureWithOrganization); } @Test public void disclosure_updateOnTheFly() { - ArgumentCaptor receiver = ArgumentCaptor.forClass( - BroadcastReceiver.class); - doNothing().when(mBroadcastDispatcher).registerReceiver(receiver.capture(), any()); - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false); createController(); - final KeyguardUpdateMonitorCallback monitor = mController.getKeyguardCallback(); - reset(mDisclosure); - when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null); - receiver.getValue().onReceive(mContext, new Intent()); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(R.string.do_disclosure_generic); - verifyNoMoreInteractions(mDisclosure); - reset(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureGeneric); + reset(mRotateTextViewController); when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME); - receiver.getValue().onReceive(mContext, new Intent()); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(mDisclosureWithOrganization); - verifyNoMoreInteractions(mDisclosure); - reset(mDisclosure); + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mDisclosureWithOrganization); + reset(mRotateTextViewController); when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false); - receiver.getValue().onReceive(mContext, new Intent()); + sendUpdateDisclosureBroadcast(); - verify(mDisclosure).setVisibility(View.GONE); - verifyNoMoreInteractions(mDisclosure); + verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_DISCLOSURE); } @Test public void transientIndication_holdsWakeLock_whenDozing() { createController(); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mController.showTransientIndication("Test"); assertTrue(mWakeLock.isHeld()); @@ -394,7 +385,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { public void transientIndication_releasesWakeLock_afterHiding() { createController(); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mController.showTransientIndication("Test"); mController.hideTransientIndication(); @@ -406,7 +397,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mInstrumentation.runOnMainSync(() -> { createController(); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mController.showTransientIndication("Test"); mController.hideTransientIndicationDelayed(0); }); @@ -425,7 +416,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mController.setVisible(true); mController.showTransientIndication("Test"); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); assertThat(mTextView.getText()).isEqualTo("Test"); assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE); @@ -442,7 +433,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message, BiometricSourceType.FACE); assertThat(mTextView.getText()).isEqualTo(message); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); assertThat(mTextView.getText()).isNotEqualTo(message); } @@ -457,7 +448,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { "A message", BiometricSourceType.FACE); assertThat(mTextView.getText()).isEqualTo(message); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); assertThat(mTextView.getText()).isNotEqualTo(message); } @@ -528,8 +519,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { public void setDozing_noIBatteryCalls() throws RemoteException { createController(); mController.setVisible(true); - mController.setDozing(true); - mController.setDozing(false); + mStatusBarStateListener.onDozingChanged(true); + mStatusBarStateListener.onDozingChanged(false); verify(mIBatteryStats, never()).computeChargeTimeRemaining(); } @@ -537,7 +528,6 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { public void updateMonitor_listener() { createController(); verify(mKeyguardStateController).addCallback(eq(mController)); - verify(mStatusBarStateController).addCallback(eq(mController)); verify(mKeyguardUpdateMonitor, times(2)).registerCallback(any()); } @@ -612,10 +602,14 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { 0 /* maxChargingWattage */, true /* present */); mController.getKeyguardCallback().onRefreshBatteryInfo(status); - mController.setDozing(true); + mStatusBarStateListener.onDozingChanged(true); mController.setVisible(true); String percentage = NumberFormat.getPercentInstance().format(90 / 100f); assertThat(mTextView.getText()).isEqualTo(percentage); } + + private void sendUpdateDisclosureBroadcast() { + mBroadcastReceiver.onReceive(mContext, new Intent()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java index 0e775047aef7e..6068f0d17720c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java @@ -51,21 +51,21 @@ public class KeyguardIndicationTextViewTest extends SysuiTestCase { @Test public void switchIndication_null_hideIndication() { - mKeyguardIndicationTextView.switchIndication(null /* text */); + mKeyguardIndicationTextView.switchIndication(null /* text */, null); assertThat(mKeyguardIndicationTextView.getText()).isEqualTo(""); } @Test public void switchIndication_emptyText_hideIndication() { - mKeyguardIndicationTextView.switchIndication("" /* text */); + mKeyguardIndicationTextView.switchIndication("" /* text */, null); assertThat(mKeyguardIndicationTextView.getText()).isEqualTo(""); } @Test public void switchIndication_newText_updateProperly() { - mKeyguardIndicationTextView.switchIndication("test_indication" /* text */); + mKeyguardIndicationTextView.switchIndication("test_indication" /* text */, null); assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("test_indication");