From 27bb209292bb27986f89d2d47f046d8f90b9fff2 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Mon, 16 Mar 2020 15:57:28 -0400 Subject: [PATCH 1/2] Remove injection from LockIcon. This moves the final remaining pieces into LockscreenIconController. Test: manual Fixes: 150393918 Change-Id: I6ce57905bdfcf832b92d5f2e4ce46054ff067851 --- .../systemui/statusbar/phone/LockIcon.java | 434 ++++-------------- .../phone/LockscreenLockIconController.java | 314 +++++++++++-- .../util/InjectionInflationController.java | 6 - .../phone/LockscreenIconControllerTest.java | 11 +- 4 files changed, 365 insertions(+), 400 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index cf9d43eeff73d..c5c7226c1734f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -16,12 +16,9 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; - import android.annotation.IntDef; import android.content.Context; import android.content.res.ColorStateList; -import android.content.res.Configuration; import android.graphics.Color; import android.graphics.drawable.Animatable2; import android.graphics.drawable.AnimatedVectorDrawable; @@ -30,241 +27,99 @@ import android.os.Trace; import android.provider.Settings; import android.text.TextUtils; import android.util.AttributeSet; -import android.view.ViewTreeObserver; -import android.view.accessibility.AccessibilityNodeInfo; +import android.view.ViewTreeObserver.OnPreDrawListener; import com.android.internal.graphics.ColorUtils; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.KeyguardAffordanceView; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility; -import com.android.systemui.statusbar.policy.AccessibilityController; -import com.android.systemui.statusbar.policy.KeyguardStateController; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import javax.inject.Inject; -import javax.inject.Named; - /** * Manages the different states and animations of the unlock icon. */ -public class LockIcon extends KeyguardAffordanceView implements - ViewTreeObserver.OnPreDrawListener { +public class LockIcon extends KeyguardAffordanceView { - private static final int STATE_LOCKED = 0; - private static final int STATE_LOCK_OPEN = 1; - private static final int STATE_SCANNING_FACE = 2; - private static final int STATE_BIOMETRICS_ERROR = 3; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final AccessibilityController mAccessibilityController; - private final KeyguardStateController mKeyguardStateController; - private final KeyguardBypassController mBypassController; - private final NotificationWakeUpCoordinator mWakeUpCoordinator; - private final HeadsUpManagerPhone mHeadsUpManager; - - private int mLastState = 0; - private boolean mForceUpdate; - private boolean mTransientBiometricsError; - private boolean mIsFaceUnlockState; - private boolean mSimLocked; - private int mDensity; + static final int STATE_LOCKED = 0; + static final int STATE_LOCK_OPEN = 1; + static final int STATE_SCANNING_FACE = 2; + static final int STATE_BIOMETRICS_ERROR = 3; + private float mDozeAmount; + private int mIconColor; + private StateProvider mStateProvider; + private int mOldState; private boolean mPulsing; private boolean mDozing; - private boolean mDocked; - private boolean mBlockUpdates; - private int mIconColor; - private float mDozeAmount; - private boolean mBouncerShowingScrimmed; - private boolean mWakeAndUnlockRunning; - private boolean mKeyguardShowing; - private boolean mShowingLaunchAffordance; private boolean mKeyguardJustShown; - private boolean mUpdatePending; - private boolean mBouncerPreHideAnimation; - private int mStatusBarState = StatusBarState.SHADE; + private boolean mScanningFace; - private final KeyguardStateController.Callback mKeyguardMonitorCallback = - new KeyguardStateController.Callback() { - @Override - public void onKeyguardShowingChanged() { - boolean force = false; - boolean wasShowing = mKeyguardShowing; - mKeyguardShowing = mKeyguardStateController.isShowing(); - if (!wasShowing && mKeyguardShowing && mBlockUpdates) { - mBlockUpdates = false; - force = true; - } - if (!wasShowing && mKeyguardShowing) { - mKeyguardJustShown = true; - } - update(force); - } + private final OnPreDrawListener mOnPreDrawListener = new OnPreDrawListener() { + @Override + public boolean onPreDraw() { + getViewTreeObserver().removeOnPreDrawListener(this); - @Override - public void onKeyguardFadingAwayChanged() { - if (!mKeyguardStateController.isKeyguardFadingAway()) { - mBouncerPreHideAnimation = false; - if (mBlockUpdates) { - mBlockUpdates = false; - update(true /* force */); - } - } - } + int newState = mStateProvider.getState(); + @LockAnimIndex final int lockAnimIndex = + getAnimationIndexForTransition(mOldState, newState, mPulsing, mDozing, + mKeyguardJustShown); - @Override - public void onUnlockedChanged() { - update(); - } - }; - - @Inject - public LockIcon(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - AccessibilityController accessibilityController, - KeyguardBypassController bypassController, - NotificationWakeUpCoordinator wakeUpCoordinator, - KeyguardStateController keyguardStateController, - HeadsUpManagerPhone headsUpManager) { - super(context, attrs); - mContext = context; - mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - mAccessibilityController = accessibilityController; - mBypassController = bypassController; - mWakeUpCoordinator = wakeUpCoordinator; - mKeyguardStateController = keyguardStateController; - mHeadsUpManager = headsUpManager; - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mKeyguardStateController.addCallback(mKeyguardMonitorCallback); - mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); - update(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mKeyguardStateController.removeCallback(mKeyguardMonitorCallback); - } - - /** - * If we're currently presenting an authentication error message. - */ - public void setTransientBiometricsError(boolean transientBiometricsError) { - mTransientBiometricsError = transientBiometricsError; - update(); - } - - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - final int density = newConfig.densityDpi; - if (density != mDensity) { - mDensity = density; - update(); - } - } - - public void update() { - update(false /* force */); - } - - public void update(boolean force) { - if (force) { - mForceUpdate = true; - } - if (!mUpdatePending) { - mUpdatePending = true; - getViewTreeObserver().addOnPreDrawListener(this); - } - } - - @Override - public boolean onPreDraw() { - mUpdatePending = false; - getViewTreeObserver().removeOnPreDrawListener(this); - - int state = getState(); - int lastState = mLastState; - boolean keyguardJustShown = mKeyguardJustShown; - mIsFaceUnlockState = state == STATE_SCANNING_FACE; - mLastState = state; - mKeyguardJustShown = false; - - boolean shouldUpdate = lastState != state || mForceUpdate; - if (mBlockUpdates && canBlockUpdates()) { - shouldUpdate = false; - } - if (shouldUpdate) { - mForceUpdate = false; - @LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(lastState, - state, mPulsing, mDozing, keyguardJustShown); boolean isAnim = lockAnimIndex != -1; - int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(state); + int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) + : LockIcon.getIconForState(newState); + + Drawable icon = getResources().getDrawable(iconRes); - Drawable icon = mContext.getDrawable(iconRes); - final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable - ? (AnimatedVectorDrawable) icon - : null; setImageDrawable(icon, false); - if (mIsFaceUnlockState) { - announceForAccessibility(getContext().getString( + if (mScanningFace) { + announceForAccessibility(getResources().getString( R.string.accessibility_scanning_face)); } - if (animation != null && isAnim) { + if (isAnim && icon instanceof AnimatedVectorDrawable) { + final AnimatedVectorDrawable animation = (AnimatedVectorDrawable) icon; animation.forceAnimationOnUI(); animation.clearAnimationCallbacks(); - animation.registerAnimationCallback(new Animatable2.AnimationCallback() { - @Override - public void onAnimationEnd(Drawable drawable) { - if (getDrawable() == animation && state == getState() - && doesAnimationLoop(lockAnimIndex)) { - animation.start(); - } else { - Trace.endAsyncSection("LockIcon#Animation", state); - } - } - }); - Trace.beginAsyncSection("LockIcon#Animation", state); + animation.registerAnimationCallback( + new Animatable2.AnimationCallback() { + @Override + public void onAnimationEnd(Drawable drawable) { + if (getDrawable() == animation + && newState == mStateProvider.getState() + && lockAnimIndex == LockIcon.SCANNING) { + animation.start(); + } else { + Trace.endAsyncSection("LockIcon#Animation", newState); + } + } + }); + Trace.beginAsyncSection("LockIcon#Animation", newState); animation.start(); } + + return true; } - updateDarkTint(); + }; - updateIconVisibility(); - updateClickability(); + public LockIcon(Context context, AttributeSet attrs) { + super(context, attrs); + } - return true; + void setStateProvider(StateProvider stateProvider) { + mStateProvider = stateProvider; } /** * Update the icon visibility * @return true if the visibility changed */ - boolean updateIconVisibility() { - boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked); - boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning - || mShowingLaunchAffordance; - if (mBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) { - if ((mHeadsUpManager.isHeadsUpGoingAway() || mHeadsUpManager.hasPinnedHeadsUp() - || mStatusBarState == StatusBarState.KEYGUARD) - && !mWakeUpCoordinator.getNotificationsFullyHidden()) { - invisible = true; - } - } - boolean wasInvisible = getVisibility() == INVISIBLE; - if (invisible != wasInvisible) { - setVisibility(invisible ? INVISIBLE : VISIBLE); + boolean updateIconVisibility(boolean visible) { + boolean wasVisible = getVisibility() == VISIBLE; + if (visible != wasVisible) { + setVisibility(visible ? VISIBLE : INVISIBLE); animate().cancel(); - if (!invisible) { + if (visible) { setScaleX(0); setScaleY(0); animate() @@ -280,49 +135,33 @@ public class LockIcon extends KeyguardAffordanceView implements return false; } - private boolean canBlockUpdates() { - return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway(); + void update(int oldState, boolean pulsing, boolean dozing, + boolean keyguardJustShown, boolean scanningFace) { + mOldState = oldState; + mPulsing = pulsing; + mDozing = dozing; + mKeyguardJustShown = keyguardJustShown; + mScanningFace = scanningFace; + + getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener); } - private void updateClickability() { - if (mAccessibilityController == null) { - return; - } - boolean canLock = mKeyguardStateController.isMethodSecure() - && mKeyguardStateController.canDismissLockScreen(); - boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled(); - setClickable(clickToUnlock); - setLongClickable(canLock && !clickToUnlock); - setFocusable(mAccessibilityController.isAccessibilityEnabled()); + void setDozeAmount(float dozeAmount) { + mDozeAmount = dozeAmount; + updateDarkTint(); } - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - boolean fingerprintRunning = mKeyguardUpdateMonitor.isFingerprintDetectionRunning(); - // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong - // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to - // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the - // check of whether non-strong biometric is allowed - boolean unlockingAllowed = mKeyguardUpdateMonitor - .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */); - if (fingerprintRunning && unlockingAllowed) { - AccessibilityNodeInfo.AccessibilityAction unlock - = new AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfo.ACTION_CLICK, - getContext().getString(R.string.accessibility_unlock_without_fingerprint)); - info.addAction(unlock); - info.setHintText(getContext().getString( - R.string.accessibility_waiting_for_fingerprint)); - } else if (mIsFaceUnlockState) { - //Avoid 'button' to be spoken for scanning face - info.setClassName(LockIcon.class.getName()); - info.setContentDescription(getContext().getString( - R.string.accessibility_scanning_face)); - } + void setIconColor(int iconColor) { + mIconColor = iconColor; + updateDarkTint(); } - private int getIconForState(int state) { + private void updateDarkTint() { + int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount); + setImageTintList(ColorStateList.valueOf(color)); + } + + static int getIconForState(int state) { int iconRes; switch (state) { case STATE_LOCKED: @@ -343,11 +182,7 @@ public class LockIcon extends KeyguardAffordanceView implements return iconRes; } - private boolean doesAnimationLoop(@LockAnimIndex int lockAnimIndex) { - return lockAnimIndex == SCANNING; - } - - private static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing, + static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing, boolean dozing, boolean keyguardJustShown) { // Never animate when screen is off @@ -367,43 +202,11 @@ public class LockIcon extends KeyguardAffordanceView implements return -1; } - public void setBouncerShowingScrimmed(boolean bouncerShowing) { - mBouncerShowingScrimmed = bouncerShowing; - if (mBypassController.getBypassEnabled()) { - update(); - } - } - - /** - * Animate padlock opening when bouncer challenge is solved. - */ - public void onBouncerPreHideAnimation() { - mBouncerPreHideAnimation = true; - update(); - } - - void setIconColor(int iconColor) { - mIconColor = iconColor; - updateDarkTint(); - } - - void setSimLocked(boolean simLocked) { - mSimLocked = simLocked; - } - - /** Set if the device is docked. */ - public void setDocked(boolean docked) { - if (mDocked != docked) { - mDocked = docked; - update(); - } - } - @Retention(RetentionPolicy.SOURCE) @IntDef({ERROR, UNLOCK, LOCK, SCANNING}) @interface LockAnimIndex {} - private static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3; - private static final int[][] LOCK_ANIM_RES_IDS = new int[][] { + static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3; + static final int[][] LOCK_ANIM_RES_IDS = new int[][] { { R.anim.lock_to_error, R.anim.lock_unlock, @@ -433,7 +236,7 @@ public class LockIcon extends KeyguardAffordanceView implements private int getThemedAnimationResId(@LockAnimIndex int lockAnimIndex) { final String setting = TextUtils.emptyIfNull( Settings.Secure.getString(getContext().getContentResolver(), - Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)); + Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)); if (setting.contains("com.android.theme.icon_pack.circular.android")) { return LOCK_ANIM_RES_IDS[1][lockAnimIndex]; } else if (setting.contains("com.android.theme.icon_pack.filled.android")) { @@ -444,83 +247,8 @@ public class LockIcon extends KeyguardAffordanceView implements return LOCK_ANIM_RES_IDS[0][lockAnimIndex]; } - private int getState() { - KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing - || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) { - return STATE_LOCK_OPEN; - } else if (mTransientBiometricsError) { - return STATE_BIOMETRICS_ERROR; - } else if (updateMonitor.isFaceDetectionRunning() && !mPulsing) { - return STATE_SCANNING_FACE; - } else { - return STATE_LOCKED; - } + interface StateProvider { + int getState(); } - /** - * When keyguard is in pulsing (AOD2) state. - * @param pulsing {@code true} when pulsing. - */ - public void setPulsing(boolean pulsing) { - mPulsing = pulsing; - update(); - } - - private void updateDarkTint() { - int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount); - setImageTintList(ColorStateList.valueOf(color)); - } - - /** - * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the - * icon on top of the black front scrim. - * @param wakeAndUnlock are we wake and unlocking - * @param isUnlock are we currently unlocking - */ - public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) { - if (wakeAndUnlock) { - mWakeAndUnlockRunning = true; - } - if (isUnlock && mBypassController.getBypassEnabled() && canBlockUpdates()) { - // We don't want the icon to change while we are unlocking - mBlockUpdates = true; - } - update(); - } - - /** - * When we're launching an affordance, like double pressing power to open camera. - */ - public void onShowingLaunchAffordanceChanged(boolean showing) { - mShowingLaunchAffordance = showing; - update(); - } - - /** - * Called whenever the scrims become opaque, transparent or semi-transparent. - */ - public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) { - if (mWakeAndUnlockRunning - && scrimsVisible == ScrimController.TRANSPARENT) { - mWakeAndUnlockRunning = false; - update(); - } - } - - void setDozing(boolean dozing) { - mDozing = dozing; - update(); - } - - void setDozeAmount(float dozeAmount) { - mDozeAmount = dozeAmount; - updateDarkTint(); - } - - /** Set the StatusBarState. */ - public void setStatusBarState(int statusBarState) { - mStatusBarState = statusBarState; - updateIconVisibility(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 2b1a8a472d9e0..224358550f13a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -16,11 +16,19 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.statusbar.phone.LockIcon.STATE_BIOMETRICS_ERROR; +import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCKED; +import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCK_OPEN; +import static com.android.systemui.statusbar.phone.LockIcon.STATE_SCANNING_FACE; + +import android.content.res.Configuration; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.hardware.biometrics.BiometricSourceType; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import androidx.annotation.Nullable; @@ -29,15 +37,18 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator.WakeUpListener; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.Optional; @@ -59,6 +70,22 @@ public class LockscreenLockIconController { private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; private final KeyguardBypassController mKeyguardBypassController; private final Optional mDockManager; + private final KeyguardStateController mKeyguardStateController; + private final Resources mResources; + private final HeadsUpManagerPhone mHeadsUpManagerPhone; + private boolean mKeyguardShowing; + private boolean mKeyguardJustShown; + private boolean mIsFaceUnlockState; + private boolean mBlockUpdates; + private boolean mPulsing; + private boolean mDozing; + private boolean mSimLocked; + private boolean mTransientBiometricsError; + private boolean mDocked; + private boolean mWakeAndUnlockRunning; + private boolean mShowingLaunchAffordance; + private boolean mBouncerShowingScrimmed; + private int mStatusBarState = StatusBarState.SHADE; private LockIcon mLockIcon; private View.OnAttachStateChangeListener mOnAttachStateChangeListener = @@ -69,10 +96,13 @@ public class LockscreenLockIconController { mConfigurationController.addCallback(mConfigurationListener); mNotificationWakeUpCoordinator.addListener(mWakeUpListener); mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); + mKeyguardStateController.addCallback(mKeyguardMonitorCallback); mDockManager.ifPresent(dockManager -> dockManager.addListener(mDockEventListener)); + mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); mConfigurationListener.onThemeChanged(); + update(); } @Override @@ -81,7 +111,7 @@ public class LockscreenLockIconController { mConfigurationController.removeCallback(mConfigurationListener); mNotificationWakeUpCoordinator.removeListener(mWakeUpListener); mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); - + mKeyguardStateController.removeCallback(mKeyguardMonitorCallback); mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener)); } @@ -91,23 +121,31 @@ public class LockscreenLockIconController { new StatusBarStateController.StateListener() { @Override public void onDozingChanged(boolean isDozing) { - mLockIcon.setDozing(isDozing); + setDozing(isDozing); } @Override public void onDozeAmountChanged(float linear, float eased) { - mLockIcon.setDozeAmount(eased); + if (mLockIcon != null) { + mLockIcon.setDozeAmount(eased); + } } @Override public void onStateChanged(int newState) { - mLockIcon.setStatusBarState(newState); + setStatusBarState(newState); } }; private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { + private int mDensity; + @Override public void onThemeChanged() { + if (mLockIcon == null) { + return; + } + TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( null, new int[]{ R.attr.wallpaperTextColor }, 0, 0); int iconColor = typedArray.getColor(0, Color.WHITE); @@ -117,6 +155,10 @@ public class LockscreenLockIconController { @Override public void onDensityOrFontScaleChanged() { + if (mLockIcon == null) { + return; + } + ViewGroup.LayoutParams lp = mLockIcon.getLayoutParams(); if (lp == null) { return; @@ -125,24 +167,41 @@ public class LockscreenLockIconController { lp.height = mLockIcon.getResources().getDimensionPixelSize( R.dimen.keyguard_lock_height); mLockIcon.setLayoutParams(lp); - mLockIcon.update(true /* force */); + update(true /* force */); } @Override public void onLocaleListChanged() { + if (mLockIcon == null) { + return; + } + mLockIcon.setContentDescription( mLockIcon.getResources().getText(R.string.accessibility_unlock_button)); - mLockIcon.update(true /* force */); + update(true /* force */); + } + + @Override + public void onConfigChanged(Configuration newConfig) { + final int density = newConfig.densityDpi; + if (density != mDensity) { + mDensity = density; + update(); + } } }; private final WakeUpListener mWakeUpListener = new WakeUpListener() { + @Override + public void onPulseExpansionChanged(boolean expandingChanged) { + } + @Override public void onFullyHiddenChanged(boolean isFullyHidden) { if (mKeyguardBypassController.getBypassEnabled()) { - boolean changed = mLockIcon.updateIconVisibility(); + boolean changed = updateIconVisibility(); if (changed) { - mLockIcon.update(); + update(); } } } @@ -152,30 +211,103 @@ public class LockscreenLockIconController { new KeyguardUpdateMonitorCallback() { @Override public void onSimStateChanged(int subId, int slotId, int simState) { - mLockIcon.setSimLocked(mKeyguardUpdateMonitor.isSimPinSecure()); - mLockIcon.update(); + mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); + update(); } @Override public void onKeyguardVisibilityChanged(boolean showing) { - mLockIcon.update(); + update(); } @Override public void onBiometricRunningStateChanged(boolean running, BiometricSourceType biometricSourceType) { - mLockIcon.update(); + update(); } @Override public void onStrongAuthStateChanged(int userId) { - mLockIcon.update(); + update(); } }; private final DockManager.DockEventListener mDockEventListener = - event -> mLockIcon.setDocked(event == DockManager.STATE_DOCKED - || event == DockManager.STATE_DOCKED_HIDE); + event -> { + boolean docked = + event == DockManager.STATE_DOCKED || event == DockManager.STATE_DOCKED_HIDE; + if (docked != mDocked) { + mDocked = docked; + update(); + } + }; + + private final KeyguardStateController.Callback mKeyguardMonitorCallback = + new KeyguardStateController.Callback() { + @Override + public void onKeyguardShowingChanged() { + boolean force = false; + boolean wasShowing = mKeyguardShowing; + mKeyguardShowing = mKeyguardStateController.isShowing(); + if (!wasShowing && mKeyguardShowing && mBlockUpdates) { + mBlockUpdates = false; + force = true; + } + if (!wasShowing && mKeyguardShowing) { + mKeyguardJustShown = true; + } + update(force); + } + + @Override + public void onKeyguardFadingAwayChanged() { + if (!mKeyguardStateController.isKeyguardFadingAway()) { + if (mBlockUpdates) { + mBlockUpdates = false; + update(true /* force */); + } + } + } + + @Override + public void onUnlockedChanged() { + update(); + } + }; + + private final View.AccessibilityDelegate mAccessibilityDelegate = + new View.AccessibilityDelegate() { + @Override + public void onInitializeAccessibilityNodeInfo(View host, + AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + boolean fingerprintRunning = + mKeyguardUpdateMonitor.isFingerprintDetectionRunning(); + // Only checking if unlocking with Biometric is allowed (no matter strong or + // non-strong as long as primary auth, i.e. PIN/pattern/password, is not + // required), so it's ok to pass true for isStrongBiometric to + // isUnlockingWithBiometricAllowed() to bypass the check of whether non-strong + // biometric is allowed + boolean unlockingAllowed = mKeyguardUpdateMonitor + .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */); + if (fingerprintRunning && unlockingAllowed) { + AccessibilityNodeInfo.AccessibilityAction unlock = + new AccessibilityNodeInfo.AccessibilityAction( + AccessibilityNodeInfo.ACTION_CLICK, + mResources.getString( + R.string.accessibility_unlock_without_fingerprint)); + info.addAction(unlock); + info.setHintText(mResources.getString( + R.string.accessibility_waiting_for_fingerprint)); + } else if (mIsFaceUnlockState) { + //Avoid 'button' to be spoken for scanning face + info.setClassName(LockIcon.class.getName()); + info.setContentDescription(mResources.getString( + R.string.accessibility_scanning_face)); + } + } + }; + private int mLastState; @Inject public LockscreenLockIconController(LockscreenGestureLogger lockscreenGestureLogger, @@ -188,7 +320,10 @@ public class LockscreenLockIconController { ConfigurationController configurationController, NotificationWakeUpCoordinator notificationWakeUpCoordinator, KeyguardBypassController keyguardBypassController, - @Nullable DockManager dockManager) { + @Nullable DockManager dockManager, + KeyguardStateController keyguardStateController, + @Main Resources resources, + HeadsUpManagerPhone headsUpManagerPhone) { mLockscreenGestureLogger = lockscreenGestureLogger; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; @@ -200,24 +335,31 @@ public class LockscreenLockIconController { mNotificationWakeUpCoordinator = notificationWakeUpCoordinator; mKeyguardBypassController = keyguardBypassController; mDockManager = dockManager == null ? Optional.empty() : Optional.of(dockManager); + mKeyguardStateController = keyguardStateController; + mResources = resources; + mHeadsUpManagerPhone = headsUpManagerPhone; mKeyguardIndicationController.setLockIconController(this); } /** * Associate the controller with a {@link LockIcon} + * + * TODO: change to an init method and inject the view. */ public void attach(LockIcon lockIcon) { mLockIcon = lockIcon; mLockIcon.setOnClickListener(this::handleClick); mLockIcon.setOnLongClickListener(this::handleLongClick); + mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate); + mLockIcon.setStateProvider(this::getState); if (mLockIcon.isAttachedToWindow()) { mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon); } mLockIcon.addOnAttachStateChangeListener(mOnAttachStateChangeListener); - mLockIcon.setStatusBarState(mStatusBarStateController.getState()); + setStatusBarState(mStatusBarStateController.getState()); } public LockIcon getView() { @@ -228,8 +370,10 @@ public class LockscreenLockIconController { * Called whenever the scrims become opaque, transparent or semi-transparent. */ public void onScrimVisibilityChanged(Integer scrimsVisible) { - if (mLockIcon != null) { - mLockIcon.onScrimVisibilityChanged(scrimsVisible); + if (mWakeAndUnlockRunning + && scrimsVisible == ScrimController.TRANSPARENT) { + mWakeAndUnlockRunning = false; + update(); } } @@ -237,55 +381,56 @@ public class LockscreenLockIconController { * Propagate {@link StatusBar} pulsing state. */ public void setPulsing(boolean pulsing) { - if (mLockIcon != null) { - mLockIcon.setPulsing(pulsing); - } + mPulsing = pulsing; + update(); } /** - * Called when the biometric authentication mode changes. - * - * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()} - * @param isUnlock If the type is {@link BiometricUnlockController#isBiometricUnlock()} () + * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the + * icon on top of the black front scrim. + * @param wakeAndUnlock are we wake and unlocking + * @param isUnlock are we currently unlocking */ public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) { - if (mLockIcon != null) { - mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock); + if (wakeAndUnlock) { + mWakeAndUnlockRunning = true; } + if (isUnlock && mKeyguardBypassController.getBypassEnabled() && canBlockUpdates()) { + // We don't want the icon to change while we are unlocking + mBlockUpdates = true; + } + update(); } /** * When we're launching an affordance, like double pressing power to open camera. */ public void onShowingLaunchAffordanceChanged(Boolean showing) { - if (mLockIcon != null) { - mLockIcon.onShowingLaunchAffordanceChanged(showing); - } + mShowingLaunchAffordance = showing; + update(); } /** Sets whether the bouncer is showing. */ public void setBouncerShowingScrimmed(boolean bouncerShowing) { - if (mLockIcon != null) { - mLockIcon.setBouncerShowingScrimmed(bouncerShowing); + mBouncerShowingScrimmed = bouncerShowing; + if (mKeyguardBypassController.getBypassEnabled()) { + update(); } } /** - * When {@link KeyguardBouncer} starts to be dismissed and starts to play its animation. + * Animate padlock opening when bouncer challenge is solved. */ public void onBouncerPreHideAnimation() { - if (mLockIcon != null) { - mLockIcon.onBouncerPreHideAnimation(); - } + update(); } /** * If we're currently presenting an authentication error message. */ public void setTransientBiometricsError(boolean transientBiometricsError) { - if (mLockIcon != null) { - mLockIcon.setTransientBiometricsError(transientBiometricsError); - } + mTransientBiometricsError = transientBiometricsError; + update(); } private boolean handleLongClick(View view) { @@ -306,4 +451,93 @@ public class LockscreenLockIconController { } mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); } + + private void update() { + update(false /* force */); + } + + private void update(boolean force) { + int state = getState(); + mIsFaceUnlockState = state == STATE_SCANNING_FACE; + + boolean shouldUpdate = mLastState != state || force; + if (mBlockUpdates && canBlockUpdates()) { + shouldUpdate = false; + } + if (shouldUpdate && mLockIcon != null) { + mLockIcon.update( + mLastState, mPulsing, mDozing, mKeyguardJustShown, mIsFaceUnlockState); + } + mLastState = state; + mKeyguardJustShown = false; + updateIconVisibility(); + updateClickability(); + } + + private int getState() { + if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing + || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) { + return STATE_LOCK_OPEN; + } else if (mTransientBiometricsError) { + return STATE_BIOMETRICS_ERROR; + } else if (mKeyguardUpdateMonitor.isFaceDetectionRunning() && !mPulsing) { + return STATE_SCANNING_FACE; + } else { + return STATE_LOCKED; + } + } + + private boolean canBlockUpdates() { + return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway(); + } + + private void setDozing(boolean isDozing) { + mDozing = isDozing; + update(); + } + + /** Set the StatusBarState. */ + private void setStatusBarState(int statusBarState) { + mStatusBarState = statusBarState; + updateIconVisibility(); + } + + /** + * Update the icon visibility + * @return true if the visibility changed + */ + private boolean updateIconVisibility() { + boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked); + boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning + || mShowingLaunchAffordance; + if (mKeyguardBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) { + if ((mHeadsUpManagerPhone.isHeadsUpGoingAway() + || mHeadsUpManagerPhone.hasPinnedHeadsUp() + || mStatusBarState == StatusBarState.KEYGUARD) + && !mNotificationWakeUpCoordinator.getNotificationsFullyHidden()) { + invisible = true; + } + } + + if (mLockIcon == null) { + return false; + } + + return mLockIcon.updateIconVisibility(!invisible); + } + + private void updateClickability() { + if (mAccessibilityController == null) { + return; + } + boolean canLock = mKeyguardStateController.isMethodSecure() + && mKeyguardStateController.canDismissLockScreen(); + boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled(); + if (mLockIcon != null) { + mLockIcon.setClickable(clickToUnlock); + mLockIcon.setLongClickable(canLock && !clickToUnlock); + mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled()); + } + } + } diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index 56aae17f451ef..c63712389a80d 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -34,7 +34,6 @@ import com.android.systemui.qs.QuickStatusBarHeader; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; -import com.android.systemui.statusbar.phone.LockIcon; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -147,11 +146,6 @@ public class InjectionInflationController { */ KeyguardMessageArea createKeyguardMessageArea(); - /** - * Creates the keyguard LockIcon. - */ - LockIcon createLockIcon(); - /** * Creates the QSPanel. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java index 487885ac244e6..85b5d70c883c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java @@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.res.Resources; import android.view.View; import androidx.test.filters.SmallTest; @@ -35,6 +36,7 @@ import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Test; @@ -71,6 +73,12 @@ public class LockscreenIconControllerTest extends SysuiTestCase { private KeyguardBypassController mKeyguardBypassController; @Mock private DockManager mDockManager; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private Resources mResources; + @Mock + private HeadsUpManagerPhone mHeadsUpManagerPhone; @Before @@ -81,7 +89,8 @@ public class LockscreenIconControllerTest extends SysuiTestCase { mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils, mShadeController, mAccessibilityController, mKeyguardIndicationController, mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator, - mKeyguardBypassController, mDockManager); + mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources, + mHeadsUpManagerPhone); mLockIconController.attach(mLockIcon); } From c46982833c52a93a8c30b85426ec2e5b377ce4eb Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Thu, 19 Mar 2020 11:26:33 -0400 Subject: [PATCH 2/2] Cache icon drawables. There's only a few of these. They currently get reloaded every time the view changes states. This loading also happens in an on-predraw listener. Cache them instead. Fixes: 145666518 Test: atest SystemUITests Change-Id: I5727459d395f1e347db42d91b313913c079ff3be --- .../systemui/statusbar/phone/LockIcon.java | 52 ++++++++++++------- .../phone/LockscreenLockIconController.java | 10 ++-- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index c5c7226c1734f..d70484e9cf415 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone; import android.annotation.IntDef; import android.content.Context; import android.content.res.ColorStateList; +import android.content.res.Configuration; import android.graphics.Color; import android.graphics.drawable.Animatable2; import android.graphics.drawable.AnimatedVectorDrawable; @@ -27,6 +28,7 @@ import android.os.Trace; import android.provider.Settings; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.SparseArray; import android.view.ViewTreeObserver.OnPreDrawListener; import com.android.internal.graphics.ColorUtils; @@ -53,7 +55,7 @@ public class LockIcon extends KeyguardAffordanceView { private boolean mPulsing; private boolean mDozing; private boolean mKeyguardJustShown; - private boolean mScanningFace; + private final SparseArray mDrawableCache = new SparseArray<>(); private final OnPreDrawListener mOnPreDrawListener = new OnPreDrawListener() { @Override @@ -61,23 +63,15 @@ public class LockIcon extends KeyguardAffordanceView { getViewTreeObserver().removeOnPreDrawListener(this); int newState = mStateProvider.getState(); - @LockAnimIndex final int lockAnimIndex = - getAnimationIndexForTransition(mOldState, newState, mPulsing, mDozing, - mKeyguardJustShown); - - boolean isAnim = lockAnimIndex != -1; - int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) - : LockIcon.getIconForState(newState); - - Drawable icon = getResources().getDrawable(iconRes); - + Drawable icon = getIcon(newState); setImageDrawable(icon, false); - if (mScanningFace) { + + if (newState == STATE_SCANNING_FACE) { announceForAccessibility(getResources().getString( R.string.accessibility_scanning_face)); } - if (isAnim && icon instanceof AnimatedVectorDrawable) { + if (icon instanceof AnimatedVectorDrawable) { final AnimatedVectorDrawable animation = (AnimatedVectorDrawable) icon; animation.forceAnimationOnUI(); animation.clearAnimationCallbacks(); @@ -87,7 +81,7 @@ public class LockIcon extends KeyguardAffordanceView { public void onAnimationEnd(Drawable drawable) { if (getDrawable() == animation && newState == mStateProvider.getState() - && lockAnimIndex == LockIcon.SCANNING) { + && newState == STATE_SCANNING_FACE) { animation.start(); } else { Trace.endAsyncSection("LockIcon#Animation", newState); @@ -110,6 +104,12 @@ public class LockIcon extends KeyguardAffordanceView { mStateProvider = stateProvider; } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mDrawableCache.clear(); + } + /** * Update the icon visibility * @return true if the visibility changed @@ -135,13 +135,11 @@ public class LockIcon extends KeyguardAffordanceView { return false; } - void update(int oldState, boolean pulsing, boolean dozing, - boolean keyguardJustShown, boolean scanningFace) { + void update(int oldState, boolean pulsing, boolean dozing, boolean keyguardJustShown) { mOldState = oldState; mPulsing = pulsing; mDozing = dozing; mKeyguardJustShown = keyguardJustShown; - mScanningFace = scanningFace; getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener); } @@ -151,7 +149,8 @@ public class LockIcon extends KeyguardAffordanceView { updateDarkTint(); } - void setIconColor(int iconColor) { + void onThemeChange(int iconColor) { + mDrawableCache.clear(); mIconColor = iconColor; updateDarkTint(); } @@ -161,6 +160,21 @@ public class LockIcon extends KeyguardAffordanceView { setImageTintList(ColorStateList.valueOf(color)); } + private Drawable getIcon(int newState) { + @LockAnimIndex final int lockAnimIndex = + getAnimationIndexForTransition(mOldState, newState, mPulsing, mDozing, + mKeyguardJustShown); + + boolean isAnim = lockAnimIndex != -1; + int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(newState); + + if (!mDrawableCache.contains(iconRes)) { + mDrawableCache.put(iconRes, getResources().getDrawable(iconRes)); + } + + return mDrawableCache.get(iconRes); + } + static int getIconForState(int state) { int iconRes; switch (state) { @@ -206,7 +220,7 @@ public class LockIcon extends KeyguardAffordanceView { @IntDef({ERROR, UNLOCK, LOCK, SCANNING}) @interface LockAnimIndex {} static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3; - static final int[][] LOCK_ANIM_RES_IDS = new int[][] { + private static final int[][] LOCK_ANIM_RES_IDS = new int[][] { { R.anim.lock_to_error, R.anim.lock_unlock, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 224358550f13a..f7c861b84a685 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -75,7 +75,6 @@ public class LockscreenLockIconController { private final HeadsUpManagerPhone mHeadsUpManagerPhone; private boolean mKeyguardShowing; private boolean mKeyguardJustShown; - private boolean mIsFaceUnlockState; private boolean mBlockUpdates; private boolean mPulsing; private boolean mDozing; @@ -150,7 +149,7 @@ public class LockscreenLockIconController { null, new int[]{ R.attr.wallpaperTextColor }, 0, 0); int iconColor = typedArray.getColor(0, Color.WHITE); typedArray.recycle(); - mLockIcon.setIconColor(iconColor); + mLockIcon.onThemeChange(iconColor); } @Override @@ -299,7 +298,7 @@ public class LockscreenLockIconController { info.addAction(unlock); info.setHintText(mResources.getString( R.string.accessibility_waiting_for_fingerprint)); - } else if (mIsFaceUnlockState) { + } else if (getState() == STATE_SCANNING_FACE) { //Avoid 'button' to be spoken for scanning face info.setClassName(LockIcon.class.getName()); info.setContentDescription(mResources.getString( @@ -458,15 +457,12 @@ public class LockscreenLockIconController { private void update(boolean force) { int state = getState(); - mIsFaceUnlockState = state == STATE_SCANNING_FACE; - boolean shouldUpdate = mLastState != state || force; if (mBlockUpdates && canBlockUpdates()) { shouldUpdate = false; } if (shouldUpdate && mLockIcon != null) { - mLockIcon.update( - mLastState, mPulsing, mDozing, mKeyguardJustShown, mIsFaceUnlockState); + mLockIcon.update(mLastState, mPulsing, mDozing, mKeyguardJustShown); } mLastState = state; mKeyguardJustShown = false;