diff options
author | Scott Lobdell <slobdell@google.com> | 2021-07-23 16:46:35 +0000 |
---|---|---|
committer | Scott Lobdell <slobdell@google.com> | 2021-07-23 17:56:38 +0000 |
commit | dc5ea9d31ab76ba378da9c550813e6b7d8be1e69 (patch) | |
tree | e69528a4200094e8616a1303f0f3104ce54b9415 /packages/SystemUI/src | |
parent | fef582047102b73e83831845a694f7409f33294b (diff) | |
parent | 0cf0aea05a722e3bec6fb3ea2077066edfd2fe57 (diff) |
Merge SP1A.210715.002
Change-Id: I65e2b7db097927b87c9c4023015c754033c62086
Diffstat (limited to 'packages/SystemUI/src')
57 files changed, 782 insertions, 477 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java index fcf4e4703ed3..c89cda98c8a5 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Color; -import android.hardware.biometrics.BiometricSourceType; import android.icu.text.NumberFormat; import com.android.settingslib.Utils; @@ -94,9 +93,7 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie @Override public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { if (mKeyguardShowing && !mIsCharging && charging) { - mView.animateCharge(() -> { - return mStatusBarStateController.isDozing(); - }); + mView.animateCharge(mStatusBarStateController::isDozing); } mIsCharging = charging; } @@ -127,21 +124,10 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @Override - public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, - boolean isStrongBiometric) { - // Strong auth will force the bouncer regardless of a successful face auth - if (biometricSourceType == BiometricSourceType.FACE - && mBypassController.canBypass() - && !mKeyguardUpdateMonitor.userNeedsStrongAuth()) { - mView.animateDisappear(); - } - } - - @Override public void onKeyguardVisibilityChanged(boolean showing) { mKeyguardShowing = showing; if (!mKeyguardShowing) { - // reset state (ie: after animateDisappear) + // reset state (ie: after weight animations) reset(); } } @@ -156,7 +142,6 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie mDozeAmount = mStatusBarStateController.getDozeAmount(); mBatteryController.addCallback(mBatteryCallback); mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); - mKeyguardShowing = true; mStatusBarStateController.removeCallback(mStatusBarStatePersistentListener); mStatusBarStateController.addCallback(mStatusBarStatePersistentListener); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 2096c310744d..72e502816534 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -79,18 +79,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV dozeParameters, unlockedScreenOffAnimationController, /* animateYPos= */ true); mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; mSmartspaceTransitionController = smartspaceTransitionController; - - mKeyguardStateController.addCallback(new KeyguardStateController.Callback() { - @Override - public void onKeyguardShowingChanged() { - // If we explicitly re-show the keyguard, make sure that all the child views are - // visible. They might have been animating out as part of the SmartSpace shared - // element transition. - if (keyguardStateController.isShowing()) { - mView.setChildrenAlphaExcludingClockView(1f); - } - } - }); } @Override @@ -102,12 +90,14 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV protected void onViewAttached() { mKeyguardUpdateMonitor.registerCallback(mInfoCallback); mConfigurationController.addCallback(mConfigurationListener); + mKeyguardStateController.addCallback(mKeyguardStateControllerCallback); } @Override protected void onViewDetached() { mKeyguardUpdateMonitor.removeCallback(mInfoCallback); mConfigurationController.removeCallback(mConfigurationListener); + mKeyguardStateController.removeCallback(mKeyguardStateControllerCallback); } /** @@ -276,6 +266,19 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } }; + private KeyguardStateController.Callback mKeyguardStateControllerCallback = + new KeyguardStateController.Callback() { + @Override + public void onKeyguardShowingChanged() { + // If we explicitly re-show the keyguard, make sure that all the child views are + // visible. They might have been animating out as part of the SmartSpace shared + // element transition. + if (mKeyguardStateController.isShowing()) { + mView.setChildrenAlphaExcludingClockView(1f); + } + } + }; + /** * Rect that specifies how KSV should be clipped, on its parent's coordinates. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 0503b2e80d86..5c9afa5cf4c9 100755 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -286,7 +286,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting protected boolean mTelephonyCapable; - private final boolean mAcquiredHapticEnabled; + private final boolean mAcquiredHapticEnabled = false; @Nullable private final Vibrator mVibrator; // Device provisioning state @@ -1414,11 +1414,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting public void playAcquiredHaptic() { if (mAcquiredHapticEnabled && mVibrator != null) { - String effect = Settings.Global.getString( - mContext.getContentResolver(), - "udfps_acquired_type"); - mVibrator.vibrate(UdfpsController.getVibration(effect, - UdfpsController.EFFECT_TICK), + mVibrator.vibrate(UdfpsController.EFFECT_CLICK, UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES); } } @@ -1732,8 +1728,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLockPatternUtils = lockPatternUtils; mAuthController = authController; dumpManager.registerDumpable(getClass().getName(), this); - mAcquiredHapticEnabled = Settings.Global.getInt(mContext.getContentResolver(), - "udfps_acquired", 0) == 1; mVibrator = vibrator; mHandler = new Handler(mainLooper) { diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 62cb4b9a33f5..afea27222fbb 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -24,8 +24,8 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; -import android.graphics.drawable.InsetDrawable; import android.hardware.biometrics.BiometricSourceType; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.util.DisplayMetrics; @@ -79,7 +79,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @NonNull private final DelayableExecutor mExecutor; private boolean mUdfpsEnrolled; - @NonNull private final Drawable mUnlockIcon; + @NonNull private final AnimatedVectorDrawable mFpToUnlockIcon; + @NonNull private final AnimatedVectorDrawable mLockToUnlockIcon; @NonNull private final Drawable mLockIcon; @NonNull private final CharSequence mUnlockedLabel; @NonNull private final CharSequence mLockedLabel; @@ -98,7 +99,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private float mHeightPixels; private float mWidthPixels; private float mDensity; - private int mKgBottomAreaHeight; + private int mAmbientIndicationHeight; // in pixels + private int mKgIndicationHeight; // in pixels private boolean mShowUnlockIcon; private boolean mShowLockIcon; @@ -132,14 +134,14 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mExecutor = executor; final Context context = view.getContext(); - mUnlockIcon = new InsetDrawable(context.getResources().getDrawable( - com.android.internal.R.drawable.ic_lock_open, context.getTheme()), - context.getResources().getDimensionPixelSize( - com.android.systemui.R.dimen.udfps_unlock_icon_inset)); - mLockIcon = new InsetDrawable(context.getResources().getDrawable( - com.android.internal.R.drawable.ic_lock, context.getTheme()), - context.getResources().getDimensionPixelSize( - com.android.systemui.R.dimen.udfps_unlock_icon_inset)); + mLockIcon = mView.getContext().getResources().getDrawable( + R.anim.lock_to_unlock, + mView.getContext().getTheme()); + mFpToUnlockIcon = (AnimatedVectorDrawable) mView.getContext().getResources().getDrawable( + R.anim.fp_to_unlock, mView.getContext().getTheme()); + mLockToUnlockIcon = (AnimatedVectorDrawable) mView.getContext().getResources().getDrawable( + R.anim.lock_to_unlock, + mView.getContext().getTheme()); mUnlockedLabel = context.getResources().getString(R.string.accessibility_unlock_button); mLockedLabel = context.getResources().getString(R.string.accessibility_lock_icon); dumpManager.registerDumpable("LockIconViewController", this); @@ -211,6 +213,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme return; } + boolean wasShowingFpIcon = mHasUdfps && !mShowUnlockIcon && !mShowLockIcon; + boolean wasShowingLockIcon = mShowLockIcon; mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen() && (!mUdfpsEnrolled || !mRunningFPS); mShowUnlockIcon = mCanDismissLockScreen && isLockScreen(); @@ -221,7 +225,15 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mView.setVisibility(View.VISIBLE); mView.setContentDescription(mLockedLabel); } else if (mShowUnlockIcon) { - mView.setImageDrawable(mUnlockIcon); + if (wasShowingFpIcon) { + mView.setImageDrawable(mFpToUnlockIcon); + mFpToUnlockIcon.forceAnimationOnUI(); + mFpToUnlockIcon.start(); + } else if (wasShowingLockIcon) { + mView.setImageDrawable(mLockToUnlockIcon); + mLockToUnlockIcon.forceAnimationOnUI(); + mLockToUnlockIcon.start(); + } mView.setVisibility(View.VISIBLE); mView.setContentDescription(mUnlockedLabel); } else { @@ -271,7 +283,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private void updateColors() { final int color = Utils.getColorAttrDefaultColor(mView.getContext(), R.attr.wallpaperTextColorAccent); - mUnlockIcon.setTint(color); + mFpToUnlockIcon.setTint(color); + mLockToUnlockIcon.setTint(color); mLockIcon.setTint(color); } @@ -280,7 +293,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mWidthPixels = metrics.widthPixels; mHeightPixels = metrics.heightPixels; mDensity = metrics.density; - mKgBottomAreaHeight = mView.getContext().getResources().getDimensionPixelSize( + mKgIndicationHeight = mView.getContext().getResources().getDimensionPixelSize( R.dimen.keyguard_indication_margin_bottom) + mView.getContext().getResources().getDimensionPixelSize( R.dimen.keyguard_indication_bottom_padding); @@ -295,15 +308,24 @@ public class LockIconViewController extends ViewController<LockIconView> impleme } else { final float distAboveKgBottomArea = 12 * mDensity; final float radius = 36 * mDensity; + final int kgBottomAreaHeight = Math.max(mKgIndicationHeight, mAmbientIndicationHeight); mView.setCenterLocation( new PointF(mWidthPixels / 2, - mHeightPixels - mKgBottomAreaHeight - distAboveKgBottomArea + mHeightPixels - kgBottomAreaHeight - distAboveKgBottomArea - radius / 2), (int) radius); } mView.getHitRect(mSensorTouchLocation); } + /** + * Set the location of ambient indication if showing (ie: now playing) + */ + public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) { + mAmbientIndicationHeight = ambientIndicationBottomPadding; + updateLockIconLocation(); + } + @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java index 45ee4ad9ae50..ee602bc9cb78 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java @@ -23,6 +23,8 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.systemui.R; public class AuthBiometricFingerprintView extends AuthBiometricView { @@ -94,12 +96,37 @@ public class AuthBiometricFingerprintView extends AuthBiometricView { mIconView.setImageDrawable(icon); + final CharSequence iconContentDescription = getIconContentDescription(newState); + if (iconContentDescription != null) { + mIconView.setContentDescription(iconContentDescription); + } + if (animation != null && shouldAnimateForTransition(lastState, newState)) { animation.forceAnimationOnUI(); animation.start(); } } + @Nullable + private CharSequence getIconContentDescription(int newState) { + switch (newState) { + case STATE_IDLE: + case STATE_AUTHENTICATING_ANIMATING_IN: + case STATE_AUTHENTICATING: + case STATE_PENDING_CONFIRMATION: + case STATE_AUTHENTICATED: + return mContext.getString( + R.string.accessibility_fingerprint_dialog_fingerprint_icon); + + case STATE_ERROR: + case STATE_HELP: + return mContext.getString(R.string.biometric_dialog_try_again); + + default: + return null; + } + } + private boolean shouldAnimateForTransition(int oldState, int newState) { switch (newState) { case STATE_HELP: diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index bebf813e1833..60b06378a61a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -636,7 +636,6 @@ public abstract class AuthBiometricView extends LinearLayout { mIndicatorView.setText(message); mIndicatorView.setTextColor(mTextColorError); mIndicatorView.setVisibility(View.VISIBLE); - mIndicatorView.setSelected(true); mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError()); Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 2aa89e86d5c8..71e2bb657de4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -48,10 +48,7 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.util.Log; -import android.view.Display; import android.view.MotionEvent; -import android.view.OrientationEventListener; -import android.view.Surface; import android.view.WindowManager; import com.android.internal.R; @@ -72,6 +69,8 @@ import java.util.Set; import javax.inject.Inject; import javax.inject.Provider; +import kotlin.Unit; + /** * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the * appropriate biometric UI (e.g. BiometricDialogView). @@ -107,7 +106,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, TaskStackListener mTaskStackListener; @VisibleForTesting IBiometricSysuiReceiver mReceiver; - @NonNull private final BiometricOrientationEventListener mOrientationListener; + @VisibleForTesting + @NonNull final BiometricOrientationEventListener mOrientationListener; @Nullable private final List<FaceSensorPropertiesInternal> mFaceProps; @Nullable private List<FingerprintSensorPropertiesInternal> mFpProps; @Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps; @@ -120,46 +120,6 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } } - private class BiometricOrientationEventListener extends OrientationEventListener { - @Surface.Rotation private int mLastRotation = ORIENTATION_UNKNOWN; - - BiometricOrientationEventListener(Context context) { - super(context); - - final Display display = context.getDisplay(); - if (display != null) { - mLastRotation = display.getRotation(); - } - } - - @Override - public void onOrientationChanged(int orientation) { - if (orientation == ORIENTATION_UNKNOWN) { - return; - } - - final Display display = mContext.getDisplay(); - if (display == null) { - return; - } - - final int rotation = display.getRotation(); - if (mLastRotation != rotation) { - mLastRotation = rotation; - - if (mCurrentDialog != null) { - mCurrentDialog.onOrientationChanged(); - } - if (mUdfpsController != null) { - mUdfpsController.onOrientationChanged(); - } - if (mSidefpsController != null) { - mSidefpsController.onOrientationChanged(); - } - } - } - } - @NonNull private final IFingerprintAuthenticatorsRegisteredCallback mFingerprintAuthenticatorsRegisteredCallback = @@ -204,6 +164,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, Log.w(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received"); mCurrentDialog.dismissWithoutCallback(true /* animate */); mCurrentDialog = null; + mOrientationListener.disable(); try { if (mReceiver != null) { @@ -232,6 +193,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, Log.w(TAG, "Evicting client due to: " + topPackage); mCurrentDialog.dismissWithoutCallback(true /* animate */); mCurrentDialog = null; + mOrientationListener.disable(); if (mReceiver != null) { mReceiver.onDialogDismissed( @@ -470,8 +432,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mUdfpsControllerFactory = udfpsControllerFactory; mSidefpsControllerFactory = sidefpsControllerFactory; mWindowManager = windowManager; - mOrientationListener = new BiometricOrientationEventListener(context); - mOrientationListener.enable(); + mOrientationListener = new BiometricOrientationEventListener(context, () -> { + onOrientationChanged(); + return Unit.INSTANCE; + }); mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null; @@ -666,6 +630,18 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, // BiometricService will have already sent the callback to the client in this case. // This avoids a round trip to SystemUI. So, just dismiss the dialog and we're done. mCurrentDialog = null; + mOrientationListener.disable(); + } + + /** + * Whether the user's finger is currently on udfps attempting to authenticate. + */ + public boolean isUdfpsFingerDown() { + if (mUdfpsController == null) { + return false; + } + + return mUdfpsController.isFingerDown(); } /** @@ -737,6 +713,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mReceiver = (IBiometricSysuiReceiver) args.arg2; mCurrentDialog = newDialog; mCurrentDialog.show(mWindowManager, savedState); + mOrientationListener.enable(); } private void onDialogDismissed(@DismissedReason int reason) { @@ -746,6 +723,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } mReceiver = null; mCurrentDialog = null; + mOrientationListener.disable(); } @Override @@ -758,6 +736,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mCurrentDialog.onSaveState(savedState); mCurrentDialog.dismissWithoutCallback(false /* animate */); mCurrentDialog = null; + mOrientationListener.disable(); // Only show the dialog if necessary. If it was animating out, the dialog is supposed // to send its pending callback immediately. @@ -778,6 +757,12 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } } + private void onOrientationChanged() { + if (mCurrentDialog != null) { + mCurrentDialog.onOrientationChanged(); + } + } + protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, boolean credentialAllowed, String opPackageName, boolean skipIntro, long operationId, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt new file mode 100644 index 000000000000..08ea857eb208 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt @@ -0,0 +1,60 @@ +/* + * 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.biometrics + +import android.content.Context +import android.view.OrientationEventListener + +/** + * An [OrientationEventListener] that invokes the [onOrientationChanged] callback whenever + * the orientation of the device has changed in order to keep overlays for biometric sensors + * aligned with the device's screen. + */ +class BiometricOrientationEventListener( + private val context: Context, + private val onOrientationChanged: () -> Unit +) : OrientationEventListener(context) { + + /** If actively listening (not available in base class). */ + var enabled: Boolean = false + private set + + private var lastRotation = context.display?.rotation ?: ORIENTATION_UNKNOWN + + override fun onOrientationChanged(orientation: Int) { + if (orientation == ORIENTATION_UNKNOWN) { + return + } + + val rotation = context.display?.rotation ?: return + if (lastRotation != rotation) { + lastRotation = rotation + + onOrientationChanged() + } + } + + override fun enable() { + enabled = true + super.enable() + } + + override fun disable() { + enabled = false + super.disable() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java index 436e1e4e3116..a51c2b802b91 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java @@ -41,6 +41,8 @@ import com.android.systemui.util.concurrency.DelayableExecutor; import javax.inject.Inject; +import kotlin.Unit; + /** * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events. */ @@ -52,6 +54,8 @@ public class SidefpsController { private final FingerprintManager mFingerprintManager; private final WindowManager mWindowManager; private final DelayableExecutor mFgExecutor; + @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener; + // TODO: update mDisplayHeight and mDisplayWidth for multi-display devices private final int mDisplayHeight; private final int mDisplayWidth; @@ -95,6 +99,10 @@ public class SidefpsController { mFingerprintManager = checkNotNull(fingerprintManager); mWindowManager = windowManager; mFgExecutor = fgExecutor; + mOrientationListener = new BiometricOrientationEventListener(context, () -> { + onOrientationChanged(); + return Unit.INSTANCE; + }); mSensorProps = findFirstSidefps(); checkArgument(mSensorProps != null); @@ -119,14 +127,15 @@ public class SidefpsController { mFingerprintManager.setSidefpsController(mSidefpsControllerImpl); } - void show() { + private void show() { mView = (SidefpsView) mInflater.inflate(R.layout.sidefps_view, null, false); mView.setSensorProperties(mSensorProps); mWindowManager.addView(mView, computeLayoutParams()); + mOrientationListener.enable(); } - void hide() { + private void hide() { if (mView != null) { mWindowManager.removeView(mView); mView.setOnTouchListener(null); @@ -135,13 +144,16 @@ public class SidefpsController { } else { Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden"); } + + mOrientationListener.disable(); } - void onOrientationChanged() { + private void onOrientationChanged() { // If mView is null or if view is hidden, then return. if (mView == null || !mIsVisible) { return; } + // If the overlay needs to be displayed with a new configuration, destroy the current // overlay, and re-create and show the overlay with the updated LayoutParams. hide(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 81e60f316bbd..ab3e042e9da7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -16,7 +16,7 @@ package com.android.systemui.biometrics; -import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK; +import static android.hardware.fingerprint.IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; @@ -26,7 +26,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.BroadcastReceiver; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -46,8 +45,6 @@ import android.os.SystemClock; import android.os.Trace; import android.os.VibrationEffect; import android.os.Vibrator; -import android.provider.Settings; -import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -79,6 +76,8 @@ import java.util.Optional; import javax.inject.Inject; +import kotlin.Unit; + /** * Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events, * and coordinates triggering of the high-brightness mode (HBM). @@ -118,6 +117,7 @@ public class UdfpsController implements DozeReceiver { @NonNull private final AccessibilityManager mAccessibilityManager; @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; @Nullable private final UdfpsHbmProvider mHbmProvider; + @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // sensors, this, in addition to a lot of the code here, will be updated. @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; @@ -156,16 +156,8 @@ public class UdfpsController implements DozeReceiver { .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); - public static final VibrationEffect EFFECT_TICK = - VibrationEffect.get(VibrationEffect.EFFECT_TICK); - private static final VibrationEffect EFFECT_TEXTURE_TICK = - VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); - @VisibleForTesting - static final VibrationEffect EFFECT_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); - private static final VibrationEffect EFFECT_HEAVY = - VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); - private static final VibrationEffect EFFECT_DOUBLE_CLICK = - VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); + public static final VibrationEffect EFFECT_CLICK = + VibrationEffect.get(VibrationEffect.EFFECT_CLICK); private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override @@ -314,8 +306,10 @@ public class UdfpsController implements DozeReceiver { @Override public void onReceive(Context context, Intent intent) { if (mServerRequest != null + && mServerRequest.mRequestReason != REASON_AUTH_FPM_KEYGUARD && Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { - Log.d(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received"); + Log.d(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received, mRequestReason: " + + mServerRequest.mRequestReason); mServerRequest.onUserCanceled(); mServerRequest = null; updateOverlay(); @@ -431,7 +425,6 @@ public class UdfpsController implements DozeReceiver { mTouchLogTime = SystemClock.elapsedRealtime(); mPowerManager.userActivity(SystemClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); - playStartHaptic(); handled = true; } else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) { Log.v(TAG, "onTouch | finger move: " + touchInfo); @@ -511,6 +504,10 @@ public class UdfpsController implements DozeReceiver { mHbmProvider = hbmProvider.orElse(null); screenLifecycle.addObserver(mScreenObserver); mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON; + mOrientationListener = new BiometricOrientationEventListener(context, () -> { + onOrientationChanged(); + return Unit.INSTANCE; + }); mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists @@ -542,18 +539,7 @@ public class UdfpsController implements DozeReceiver { @VisibleForTesting public void playStartHaptic() { if (mVibrator != null) { - final ContentResolver contentResolver = - mContext.getContentResolver(); - // TODO: these settings checks should eventually be removed after ux testing - // (b/185124905) - int startEnabled = Settings.Global.getInt(contentResolver, - "udfps_start", 1); - if (startEnabled > 0) { - String startEffectSetting = Settings.Global.getString( - contentResolver, "udfps_start_type"); - mVibrator.vibrate(getVibration(startEffectSetting, - EFFECT_CLICK), VIBRATION_SONIFICATION_ATTRIBUTES); - } + mVibrator.vibrate(EFFECT_CLICK, VIBRATION_SONIFICATION_ATTRIBUTES); } } @@ -626,9 +612,12 @@ public class UdfpsController implements DozeReceiver { // Gets the size based on the current rotation of the display. mContext.getDisplay().getRealSize(p); - // Transform dimensions if the device is in landscape mode. + // Transform dimensions if the device is in landscape mode switch (mContext.getDisplay().getRotation()) { case Surface.ROTATION_90: + if (animation instanceof UdfpsKeyguardViewController) { + break; + } mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius - paddingX; mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius @@ -636,6 +625,9 @@ public class UdfpsController implements DozeReceiver { break; case Surface.ROTATION_270: + if (animation instanceof UdfpsKeyguardViewController) { + break; + } mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius - paddingX; mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius @@ -644,13 +636,14 @@ public class UdfpsController implements DozeReceiver { default: // Do nothing to stay in portrait mode. + // Keyguard is always in portrait mode. } // avoid announcing window title mCoreLayoutParams.accessibilityTitle = " "; return mCoreLayoutParams; } - void onOrientationChanged() { + private void onOrientationChanged() { // When the configuration changes it's almost always necessary to destroy and re-create // the overlay's window to pass it the new LayoutParams. // Hiding the overlay will destroy its window. It's safe to hide the overlay regardless @@ -668,6 +661,7 @@ public class UdfpsController implements DozeReceiver { if (mView == null) { try { Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason); + mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false); mOnFingerDown = false; mView.setSensorProperties(mSensorProps); @@ -675,11 +669,13 @@ public class UdfpsController implements DozeReceiver { UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason); animation.init(); mView.setAnimationViewController(animation); + mOrientationListener.enable(); // This view overlaps the sensor area, so prevent it from being selectable // during a11y. if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR - || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) { + || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING + || reason == IUdfpsOverlayController.REASON_AUTH_BP) { mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); } @@ -768,6 +764,8 @@ public class UdfpsController implements DozeReceiver { } else { Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden"); } + + mOrientationListener.disable(); } /** @@ -822,12 +820,19 @@ public class UdfpsController implements DozeReceiver { mIsAodInterruptActive = false; } + public boolean isFingerDown() { + return mOnFingerDown; + } + private void onFingerDown(int x, int y, float minor, float major) { mExecution.assertIsMainThread(); if (mView == null) { Log.w(TAG, "Null view in onFingerDown"); return; } + if (!mOnFingerDown) { + playStartHaptic(); + } mOnFingerDown = true; mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major); Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0); @@ -855,38 +860,6 @@ public class UdfpsController implements DozeReceiver { } } - /** - * get vibration to play given string - * used for testing purposes (b/185124905) - */ - public static VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) { - if (TextUtils.isEmpty(effect)) { - return defaultEffect; - } - - switch (effect.toLowerCase()) { - case "click": - return EFFECT_CLICK; - case "heavy": - return EFFECT_HEAVY; - case "texture_tick": - return EFFECT_TEXTURE_TICK; - case "tick": - return EFFECT_TICK; - case "double_tap": - return EFFECT_DOUBLE_CLICK; - default: - try { - int primitive = Integer.parseInt(effect); - if (primitive <= PRIMITIVE_LOW_TICK && primitive > -1) { - return VibrationEffect.startComposition().addPrimitive(primitive).compose(); - } - } catch (NumberFormatException e) { - } - return defaultEffect; - } - } - private void updateTouchListener() { if (mView == null) { return; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java index ec96af3ef500..131618442c22 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.airbnb.lottie.LottieAnimationView; import com.airbnb.lottie.LottieProperty; import com.airbnb.lottie.model.KeyPath; + /** * View corresponding with udfps_keyguard_view.xml */ @@ -114,12 +115,10 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { @Override void onIlluminationStarting() { - setVisibility(View.INVISIBLE); } @Override void onIlluminationStopped() { - setVisibility(View.VISIBLE); } @Override @@ -140,9 +139,12 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { mAodFp.setTranslationX(mBurnInOffsetX); mAodFp.setTranslationY(mBurnInOffsetY); mAodFp.setProgress(mBurnInProgress); + mAodFp.setAlpha(255 * mInterpolatedDarkAmount); mLockScreenFp.setTranslationX(mBurnInOffsetX); mLockScreenFp.setTranslationY(mBurnInOffsetY); + mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount); + mLockScreenFp.setAlpha((1f - mInterpolatedDarkAmount) * 255); } void requestUdfps(boolean request, int color) { @@ -209,14 +211,6 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { mHintAnimator.cancel(); mInterpolatedDarkAmount = eased; updateBurnInOffsets(); - mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount); - mAodFp.setAlpha(mInterpolatedDarkAmount); - - if (linear == 1f) { - mLockScreenFp.setVisibility(View.INVISIBLE); - } else { - mLockScreenFp.setVisibility(View.VISIBLE); - } } void animateHint() { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 819de538c840..51124fb28ad1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -41,6 +41,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor; import java.io.FileDescriptor; import java.io.PrintWriter; + /** * Class that coordinates non-HBM animations during keyguard authentication. * @@ -66,6 +67,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud private boolean mHintShown; private int mStatusBarState; private float mTransitionToFullShadeProgress; + private float mLastDozeAmount; /** * hidden amount of pin/pattern/password bouncer @@ -108,6 +110,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud updateFaceDetectRunning(mKeyguardUpdateMonitor.isFaceDetectionRunning()); final float dozeAmount = mStatusBarStateController.getDozeAmount(); + mLastDozeAmount = dozeAmount; mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount); mStatusBarStateController.addCallback(mStateListener); @@ -205,7 +208,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud return true; } - if (mInputBouncerHiddenAmount < .4f || mIsBouncerVisible) { + if (mInputBouncerHiddenAmount < .5f || mIsBouncerVisible) { return true; } @@ -277,8 +280,9 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud private void updateAlpha() { // fade icon on transition to showing bouncer int alpha = mShowingUdfpsBouncer ? 255 - : Math.abs((int) MathUtils.constrainedMap(0f, 255f, .4f, .7f, - mInputBouncerHiddenAmount)); + : (int) MathUtils.constrain( + MathUtils.map(.5f, .9f, 0f, 255f, mInputBouncerHiddenAmount), + 0f, 255f); alpha *= (1.0f - mTransitionToFullShadeProgress); mView.setUnpausedAlpha(alpha); } @@ -287,8 +291,11 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud new StatusBarStateController.StateListener() { @Override public void onDozeAmountChanged(float linear, float eased) { - if (linear != 0) showUdfpsBouncer(false); + if (mLastDozeAmount < linear) { + showUdfpsBouncer(false); + } mView.onDozeAmountChanged(linear, eased); + mLastDozeAmount = linear; updatePauseAuth(); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java index 809e7a70d66c..37a6cfaabb5e 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java @@ -33,7 +33,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.systemui.classifier.FalsingDataProvider.SessionListener; import com.android.systemui.classifier.HistoryTracker.BeliefListener; import com.android.systemui.dagger.qualifiers.TestHarness; -import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -66,7 +65,6 @@ public class BrightLineFalsingManager implements FalsingManager { private static final double FALSE_BELIEF_THRESHOLD = 0.9; private final FalsingDataProvider mDataProvider; - private final DockManager mDockManager; private final SingleTapClassifier mSingleTapClassifier; private final DoubleTapClassifier mDoubleTapClassifier; private final HistoryTracker mHistoryTracker; @@ -173,14 +171,13 @@ public class BrightLineFalsingManager implements FalsingManager { @Inject public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider, - DockManager dockManager, MetricsLogger metricsLogger, + MetricsLogger metricsLogger, @Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers, SingleTapClassifier singleTapClassifier, DoubleTapClassifier doubleTapClassifier, HistoryTracker historyTracker, KeyguardStateController keyguardStateController, AccessibilityManager accessibilityManager, @TestHarness boolean testHarness) { mDataProvider = falsingDataProvider; - mDockManager = dockManager; mMetricsLogger = metricsLogger; mClassifiers = classifiers; mSingleTapClassifier = singleTapClassifier; @@ -332,7 +329,7 @@ public class BrightLineFalsingManager implements FalsingManager { || !mKeyguardStateController.isShowing() || mTestHarness || mDataProvider.isJustUnlockedWithFace() - || mDockManager.isDocked() + || mDataProvider.isDocked() || mAccessibilityManager.isEnabled(); } @@ -400,7 +397,7 @@ public class BrightLineFalsingManager implements FalsingManager { ipw.print("mJustUnlockedWithFace="); ipw.println(mDataProvider.isJustUnlockedWithFace() ? 1 : 0); ipw.print("isDocked="); - ipw.println(mDockManager.isDocked() ? 1 : 0); + ipw.println(mDataProvider.isDocked() ? 1 : 0); ipw.print("width="); ipw.println(mDataProvider.getWidthPixels()); ipw.print("height="); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java index 969736910b5e..14e5991f35d2 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.classifier; +import static com.android.systemui.dock.DockManager.DockEventListener; + import android.hardware.SensorManager; import android.hardware.biometrics.BiometricSourceType; import android.util.Log; @@ -25,9 +27,12 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.sensors.ProximitySensor; @@ -53,6 +58,8 @@ class FalsingCollectorImpl implements FalsingCollector { private final ProximitySensor mProximitySensor; private final StatusBarStateController mStatusBarStateController; private final KeyguardStateController mKeyguardStateController; + private final BatteryController mBatteryController; + private final DockManager mDockManager; private final DelayableExecutor mMainExecutor; private final SystemClock mSystemClock; @@ -89,12 +96,46 @@ class FalsingCollectorImpl implements FalsingCollector { } }; + + private final BatteryStateChangeCallback mBatteryListener = new BatteryStateChangeCallback() { + @Override + public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { + } + + @Override + public void onWirelessChargingChanged(boolean isWirelessCharging) { + if (isWirelessCharging || mDockManager.isDocked()) { + mProximitySensor.pause(); + } else { + mProximitySensor.resume(); + } + } + }; + + private final DockEventListener mDockEventListener = new DockEventListener() { + @Override + public void onEvent(int event) { + if (event == DockManager.STATE_NONE && !mBatteryController.isWirelessCharging()) { + mProximitySensor.resume(); + } else { + mProximitySensor.pause(); + } + } + }; + @Inject - FalsingCollectorImpl(FalsingDataProvider falsingDataProvider, FalsingManager falsingManager, - KeyguardUpdateMonitor keyguardUpdateMonitor, HistoryTracker historyTracker, - ProximitySensor proximitySensor, StatusBarStateController statusBarStateController, + FalsingCollectorImpl( + FalsingDataProvider falsingDataProvider, + FalsingManager falsingManager, + KeyguardUpdateMonitor keyguardUpdateMonitor, + HistoryTracker historyTracker, + ProximitySensor proximitySensor, + StatusBarStateController statusBarStateController, KeyguardStateController keyguardStateController, - @Main DelayableExecutor mainExecutor, SystemClock systemClock) { + BatteryController batteryController, + DockManager dockManager, + @Main DelayableExecutor mainExecutor, + SystemClock systemClock) { mFalsingDataProvider = falsingDataProvider; mFalsingManager = falsingManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; @@ -102,10 +143,11 @@ class FalsingCollectorImpl implements FalsingCollector { mProximitySensor = proximitySensor; mStatusBarStateController = statusBarStateController; mKeyguardStateController = keyguardStateController; + mBatteryController = batteryController; + mDockManager = dockManager; mMainExecutor = mainExecutor; mSystemClock = systemClock; - mProximitySensor.setTag(PROXIMITY_SENSOR_TAG); mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME); @@ -113,6 +155,9 @@ class FalsingCollectorImpl implements FalsingCollector { mState = mStatusBarStateController.getState(); mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); + + mBatteryController.addCallback(mBatteryListener); + mDockManager.addListener(mDockEventListener); } @Override @@ -312,6 +357,8 @@ class FalsingCollectorImpl implements FalsingCollector { unregisterSensors(); mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback); mStatusBarStateController.removeCallback(mStatusBarStateListener); + mBatteryController.removeCallback(mBatteryListener); + mDockManager.removeListener(mDockEventListener); } @Override @@ -351,9 +398,7 @@ class FalsingCollectorImpl implements FalsingCollector { } private void registerSensors() { - if (!mFalsingDataProvider.isWirelessCharging()) { - mProximitySensor.register(mSensorEventListener); - } + mProximitySensor.register(mSensorEventListener); } private void unregisterSensors() { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java index 2f688dd9d247..a3ecb0c0b273 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java @@ -22,6 +22,7 @@ import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.policy.BatteryController; import java.util.ArrayList; @@ -40,7 +41,8 @@ public class FalsingDataProvider { private final int mWidthPixels; private final int mHeightPixels; - private final BatteryController mBatteryController; + private BatteryController mBatteryController; + private final DockManager mDockManager; private final float mXdpi; private final float mYdpi; private final List<SessionListener> mSessionListeners = new ArrayList<>(); @@ -59,12 +61,16 @@ public class FalsingDataProvider { private boolean mJustUnlockedWithFace; @Inject - public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController) { + public FalsingDataProvider( + DisplayMetrics displayMetrics, + BatteryController batteryController, + DockManager dockManager) { mXdpi = displayMetrics.xdpi; mYdpi = displayMetrics.ydpi; mWidthPixels = displayMetrics.widthPixels; mHeightPixels = displayMetrics.heightPixels; mBatteryController = batteryController; + mDockManager = dockManager; FalsingClassifier.logInfo("xdpi, ydpi: " + getXdpi() + ", " + getYdpi()); FalsingClassifier.logInfo("width, height: " + getWidthPixels() + ", " + getHeightPixels()); @@ -219,11 +225,6 @@ public class FalsingDataProvider { return mLastMotionEvent.getY() < mFirstRecentMotionEvent.getY(); } - /** Returns true if phone is being charged without a cable. */ - public boolean isWirelessCharging() { - return mBatteryController.isWirelessCharging(); - } - private void recalculateData() { if (!mDirty) { return; @@ -357,6 +358,11 @@ public class FalsingDataProvider { mJustUnlockedWithFace = justUnlockedWithFace; } + /** Returns true if phone is sitting in a dock or is wirelessly charging. */ + public boolean isDocked() { + return mBatteryController.isWirelessCharging() || mDockManager.isDocked(); + } + /** Implement to be alerted abotu the beginning and ending of falsing tracking. */ public interface SessionListener { /** Called when the lock screen is shown and falsing-tracking begins. */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 8a9fc2685a71..277ecdfef52f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1536,8 +1536,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, return; } - // if the keyguard is already showing, don't bother - if (mKeyguardViewControllerLazy.get().isShowing()) { + // if the keyguard is already showing, don't bother. check flags in both files + // to account for the hiding animation which results in a delay and discrepancy + // between flags + if (mShowing && mKeyguardViewControllerLazy.get().isShowing()) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing"); resetStateLocked(); return; @@ -2339,7 +2341,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, // only play "unlock" noises if not on a call (since the incall UI // disables the keyguard) if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) { - Log.i("TEST", "playSounds: false"); playSounds(false); } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 902e8c28a85d..15a70831b2f9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -475,6 +475,13 @@ public class MediaControlPanel { @Nullable private ActivityLaunchAnimator.Controller buildLaunchAnimatorController( TransitionLayout player) { + if (!(player.getParent() instanceof ViewGroup)) { + // TODO(b/192194319): Throw instead of just logging. + Log.wtf(TAG, "Skipping player animation as it is not attached to a ViewGroup", + new Exception()); + return null; + } + // TODO(b/174236650): Make sure that the carousel indicator also fades out. // TODO(b/174236650): Instrument the animation to measure jank. return new GhostedViewLaunchAnimatorController(player, diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index 7ab4b6f200ed..a16b92f494a4 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -47,6 +47,7 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; +import android.graphics.ImageDecoder; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.graphics.text.LineBreaker; @@ -59,6 +60,7 @@ import android.text.TextUtils; import android.util.IconDrawableFactory; import android.util.Log; import android.util.Pair; +import android.util.Size; import android.util.SizeF; import android.util.TypedValue; import android.view.Gravity; @@ -79,6 +81,7 @@ import com.android.systemui.people.widget.LaunchConversationActivity; import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; import com.android.systemui.people.widget.PeopleTileKey; +import java.io.IOException; import java.text.NumberFormat; import java.time.Duration; import java.util.ArrayList; @@ -178,6 +181,7 @@ public class PeopleTileViewHelper { private int mWidth; private int mHeight; private int mLayoutSize; + private boolean mIsLeftToRight; private Locale mLocale; private NumberFormat mIntegerFormat; @@ -192,6 +196,8 @@ public class PeopleTileViewHelper { mWidth = width; mHeight = height; mLayoutSize = getLayoutSize(); + mIsLeftToRight = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) + == View.LAYOUT_DIRECTION_LTR; } /** @@ -501,6 +507,8 @@ public class PeopleTileViewHelper { views.setViewVisibility(R.id.availability, View.VISIBLE); startPadding = mContext.getResources().getDimensionPixelSize( R.dimen.availability_dot_shown_padding); + views.setContentDescription(R.id.availability, + mContext.getString(R.string.person_available)); } else { views.setViewVisibility(R.id.availability, View.GONE); startPadding = mContext.getResources().getDimensionPixelSize( @@ -654,7 +662,7 @@ public class PeopleTileViewHelper { private RemoteViews createMissedCallRemoteViews() { RemoteViews views = setViewForContentLayout(new RemoteViews(mContext.getPackageName(), getLayoutForContent())); - views.setViewVisibility(R.id.predefined_icon, View.VISIBLE); + setPredefinedIconVisible(views); views.setViewVisibility(R.id.text_content, View.VISIBLE); views.setViewVisibility(R.id.messages_count, View.GONE); setMaxLines(views, false); @@ -673,19 +681,39 @@ public class PeopleTileViewHelper { return views; } + private void setPredefinedIconVisible(RemoteViews views) { + views.setViewVisibility(R.id.predefined_icon, View.VISIBLE); + if (mLayoutSize == LAYOUT_MEDIUM) { + int endPadding = mContext.getResources().getDimensionPixelSize( + R.dimen.before_predefined_icon_padding); + views.setViewPadding(R.id.name, mIsLeftToRight ? 0 : endPadding, 0, + mIsLeftToRight ? endPadding : 0, + 0); + } + } + private RemoteViews createNotificationRemoteViews() { RemoteViews views = setViewForContentLayout(new RemoteViews(mContext.getPackageName(), getLayoutForNotificationContent())); CharSequence sender = mTile.getNotificationSender(); - Uri image = mTile.getNotificationDataUri(); - if (image != null) { - // TODO: Use NotificationInlineImageCache - views.setImageViewUri(R.id.image, image); + Uri imageUri = mTile.getNotificationDataUri(); + if (imageUri != null) { String newImageDescription = mContext.getString( R.string.new_notification_image_content_description, mTile.getUserName()); views.setContentDescription(R.id.image, newImageDescription); views.setViewVisibility(R.id.image, View.VISIBLE); views.setViewVisibility(R.id.text_content, View.GONE); + try { + Drawable drawable = resolveImage(imageUri, mContext); + Bitmap bitmap = convertDrawableToBitmap(drawable); + views.setImageViewBitmap(R.id.image, bitmap); + } catch (IOException e) { + Log.e(TAG, "Could not decode image: " + e); + // If we couldn't load the image, show text that we have a new image. + views.setTextViewText(R.id.text_content, newImageDescription); + views.setViewVisibility(R.id.text_content, View.VISIBLE); + views.setViewVisibility(R.id.image, View.GONE); + } } else { setMaxLines(views, !TextUtils.isEmpty(sender)); CharSequence content = mTile.getNotificationContent(); @@ -703,6 +731,13 @@ public class PeopleTileViewHelper { views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_message); } if (mTile.getMessagesCount() > 1) { + if (mLayoutSize == LAYOUT_MEDIUM) { + int endPadding = mContext.getResources().getDimensionPixelSize( + R.dimen.before_messages_count_padding); + views.setViewPadding(R.id.name, mIsLeftToRight ? 0 : endPadding, 0, + mIsLeftToRight ? endPadding : 0, + 0); + } views.setViewVisibility(R.id.messages_count, View.VISIBLE); views.setTextViewText(R.id.messages_count, getMessagesCountText(mTile.getMessagesCount())); @@ -720,6 +755,40 @@ public class PeopleTileViewHelper { return views; } + private Drawable resolveImage(Uri uri, Context context) throws IOException { + final ImageDecoder.Source source = + ImageDecoder.createSource(context.getContentResolver(), uri); + final Drawable drawable = + ImageDecoder.decodeDrawable(source, (decoder, info, s) -> { + onHeaderDecoded(decoder, info, s); + }); + return drawable; + } + + private static int getPowerOfTwoForSampleRatio(double ratio) { + final int k = Integer.highestOneBit((int) Math.floor(ratio)); + return Math.max(1, k); + } + + private void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info, + ImageDecoder.Source source) { + int widthInPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mWidth, + mContext.getResources().getDisplayMetrics()); + int heightInPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mHeight, + mContext.getResources().getDisplayMetrics()); + int maxIconSizeInPx = Math.max(widthInPx, heightInPx); + int minDimen = (int) (1.5 * Math.min(widthInPx, heightInPx)); + if (minDimen < maxIconSizeInPx) { + maxIconSizeInPx = minDimen; + } + final Size size = info.getSize(); + final int originalSize = Math.max(size.getHeight(), size.getWidth()); + final double ratio = (originalSize > maxIconSizeInPx) + ? originalSize * 1f / maxIconSizeInPx + : 1.0; + decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio)); + } + private void setContentDescriptionForNotificationTextContent(RemoteViews views, CharSequence content, CharSequence sender) { String newTextDescriptionWithNotificationContent = mContext.getString( @@ -755,7 +824,7 @@ public class PeopleTileViewHelper { if (TextUtils.isEmpty(statusText)) { statusText = getStatusTextByType(status.getActivity()); } - views.setViewVisibility(R.id.predefined_icon, View.VISIBLE); + setPredefinedIconVisible(views); views.setTextViewText(R.id.text_content, statusText); if (status.getActivity() == ACTIVITY_BIRTHDAY @@ -865,13 +934,11 @@ public class PeopleTileViewHelper { * on the status layouts compared to all other layouts. */ private void setAvailabilityDotPadding(RemoteViews views, int resId) { - boolean isLeftToRight = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) - == View.LAYOUT_DIRECTION_LTR; int startPadding = mContext.getResources().getDimensionPixelSize(resId); int bottomPadding = mContext.getResources().getDimensionPixelSize( R.dimen.medium_content_padding_above_name); views.setViewPadding(R.id.medium_content, - isLeftToRight ? startPadding : 0, 0, isLeftToRight ? 0 : startPadding, + mIsLeftToRight ? startPadding : 0, 0, mIsLeftToRight ? 0 : startPadding, bottomPadding); } @@ -1069,6 +1136,7 @@ public class PeopleTileViewHelper { views.setViewPadding(R.id.content, horizontalPadding, verticalPadding, horizontalPadding, verticalPadding); + views.setViewPadding(R.id.name, 0, 0, 0, 0); // Expand the name font on medium if there's space. int heightRequiredForMaxContentText = (int) (mContext.getResources().getDimension( R.dimen.medium_height_for_max_name_text_size) / mDensity); diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 3320fbda6fe5..985903435b9a 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -375,7 +375,7 @@ public class PeopleSpaceWidgetManager { widgetSp.getInt(USER_ID, INVALID_USER_ID), widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)); - return getTileFromPersistentStorage(key, appWidgetId); + return getTileFromPersistentStorage(key, appWidgetId, /* supplementFromStorage= */ true); } /** @@ -383,7 +383,8 @@ public class PeopleSpaceWidgetManager { * If a {@link PeopleTileKey} is not provided, fetch one from {@link SharedPreferences}. */ @Nullable - public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) throws + public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId, + boolean supplementFromStorage) throws PackageManager.NameNotFoundException { if (!PeopleTileKey.isValid(key)) { Log.e(TAG, "PeopleTileKey invalid: " + key.toString()); @@ -412,7 +413,8 @@ public class PeopleSpaceWidgetManager { // Supplement with our storage. String contactUri = mSharedPrefs.getString(String.valueOf(appWidgetId), null); - if (contactUri != null && storedTile.build().getContactUri() == null) { + if (supplementFromStorage && contactUri != null + && storedTile.build().getContactUri() == null) { if (DEBUG) Log.d(TAG, "Restore contact uri from storage: " + contactUri); storedTile.setContactUri(Uri.parse(contactUri)); } @@ -811,7 +813,8 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "addNewWidget called with key for appWidgetId: " + appWidgetId); PeopleSpaceTile tile = null; try { - tile = getTileFromPersistentStorage(key, appWidgetId); + tile = getTileFromPersistentStorage(key, appWidgetId, /* supplementFromStorage= */ + false); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Cannot add widget since app was uninstalled"); return; @@ -850,7 +853,9 @@ public class PeopleSpaceWidgetManager { } catch (Exception e) { Log.w(TAG, "Exception caching shortcut:" + e); } - updateAppWidgetOptionsAndView(appWidgetId, tile); + PeopleSpaceTile finalTile = tile; + mBgExecutor.execute( + () -> updateAppWidgetOptionsAndView(appWidgetId, finalTile)); } /** Registers a conversation listener for {@code appWidgetId} if not already registered. */ @@ -1071,7 +1076,7 @@ public class PeopleSpaceWidgetManager { return; } for (int appWidgetId : appWidgetIds) { - if (DEBUG) Log.d(TAG, "Updating widget from broadcast, widget id: " + appWidgetId); + if (DEBUG) Log.d(TAG, "Updating widget from broadcast, widget id: " + appWidgetId); PeopleSpaceTile existingTile = null; PeopleSpaceTile updatedTile = null; try { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java index bcce87a51097..929927e5d4e4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java @@ -44,6 +44,7 @@ import com.android.systemui.Dependency; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; @@ -57,6 +58,7 @@ public class QSDetail extends LinearLayout { private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger(); private ViewGroup mDetailContent; + private FalsingManager mFalsingManager; protected TextView mDetailSettingsButton; protected TextView mDetailDoneButton; @VisibleForTesting @@ -124,12 +126,13 @@ public class QSDetail extends LinearLayout { /** */ public void setQsPanel(QSPanelController panelController, QuickStatusBarHeader header, - QSFooter footer) { + QSFooter footer, FalsingManager falsingManager) { mQsPanelController = panelController; mHeader = header; mFooter = footer; mHeader.setCallback(mQsPanelCallback); mQsPanelController.setCallback(mQsPanelCallback); + mFalsingManager = falsingManager; } public void setHost(QSTileHost host) { @@ -209,6 +212,11 @@ public class QSDetail extends LinearLayout { Dependency.get(CommandQueue.class).animateCollapsePanels(); mTriggeredExpand = false; } + // Always animate on close, even if the last opened detail adapter had shouldAnimate() + // return false. This is necessary to avoid a race condition which could leave the + // keyguard in a bad state where QS remains visible underneath the notifications, clock, + // and status area. + mShouldAnimate = true; } boolean visibleDiff = wasShowingDetail != showingDetail; @@ -242,10 +250,15 @@ public class QSDetail extends LinearLayout { mClosingDetail = true; mDetailAdapter = null; listener = mTeardownDetailWhenDone; - mHeader.setVisibility(View.VISIBLE); - mFooter.setVisibility(View.VISIBLE); - mQsPanelController.setGridContentVisibility(true); - mQsPanelCallback.onScanStateChanged(false); + // Only update visibility if already expanded. Otherwise, a race condition can cause the + // keyguard to enter a bad state where the QS tiles are displayed underneath the + // notifications, clock, and status area. + if (mQsPanelController.isExpanded()) { + mHeader.setVisibility(View.VISIBLE); + mFooter.setVisibility(View.VISIBLE); + mQsPanelController.setGridContentVisibility(true); + mQsPanelCallback.onScanStateChanged(false); + } } sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); animateDetailVisibleDiff(x, y, visibleDiff, listener); @@ -273,6 +286,9 @@ public class QSDetail extends LinearLayout { final Intent settingsIntent = adapter.getSettingsIntent(); mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE); mDetailSettingsButton.setOnClickListener(v -> { + if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + return; + } Dependency.get(MetricsLogger.class).action(ACTION_QS_MORE_SETTINGS, adapter.getMetricsCategory()); mUiEventLogger.log(adapter.moreSettingsEvent()); @@ -280,6 +296,9 @@ public class QSDetail extends LinearLayout { .postStartActivityDismissingKeyguard(settingsIntent, 0); }); mDetailDoneButton.setOnClickListener(v -> { + if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + return; + } announceForAccessibility( mContext.getString(R.string.accessibility_desc_quick_settings)); if (!adapter.onDoneButtonClicked()) { @@ -301,13 +320,13 @@ public class QSDetail extends LinearLayout { mQsDetailHeaderSwitch.setVisibility(VISIBLE); handleToggleStateChanged(toggleState, adapter.getToggleEnabled()); mQsDetailHeader.setClickable(true); - mQsDetailHeader.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - boolean checked = !mQsDetailHeaderSwitch.isChecked(); - mQsDetailHeaderSwitch.setChecked(checked); - adapter.setToggleState(checked); + mQsDetailHeader.setOnClickListener(v -> { + if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + return; } + boolean checked = !mQsDetailHeaderSwitch.isChecked(); + mQsDetailHeaderSwitch.setChecked(checked); + adapter.setToggleState(checked); }); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 36b4ee987d99..0a1e9d04fab0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -39,6 +39,7 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.media.MediaHost; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.customize.QSCustomizerController; @@ -67,6 +68,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private final Rect mQsBounds = new Rect(); private final StatusBarStateController mStatusBarStateController; + private final FalsingManager mFalsingManager; private boolean mQsExpanded; private boolean mHeaderAnimating; private boolean mStackScrollerOverscrolling; @@ -133,7 +135,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca StatusBarStateController statusBarStateController, CommandQueue commandQueue, QSDetailDisplayer qsDetailDisplayer, @Named(QS_PANEL) MediaHost qsMediaHost, @Named(QUICK_QS_PANEL) MediaHost qqsMediaHost, - QSFragmentComponent.Factory qsComponentFactory, FeatureFlags featureFlags) { + QSFragmentComponent.Factory qsComponentFactory, FeatureFlags featureFlags, + FalsingManager falsingManager) { mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler; mInjectionInflater = injectionInflater; mCommandQueue = commandQueue; @@ -144,6 +147,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca commandQueue.observe(getLifecycle(), this); mHost = qsTileHost; mFeatureFlags = featureFlags; + mFalsingManager = falsingManager; mStatusBarStateController = statusBarStateController; } @@ -190,7 +194,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mQSContainerImplController.init(); mContainer = mQSContainerImplController.getView(); - mQSDetail.setQsPanel(mQSPanelController, mHeader, mFooter); + mQSDetail.setQsPanel(mQSPanelController, mHeader, mFooter, mFalsingManager); mQSAnimator = qsFragmentComponent.getQSAnimator(); mQSCustomizerController = qsFragmentComponent.getQSCustomizerController(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index 82b6c0c1805d..ab81ac1fd577 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -153,9 +153,25 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { }); } + @Nullable + private CharSequence getServiceLabelSafe() { + try { + return mController.getWalletClient().getServiceLabel(); + } catch (RuntimeException e) { + Log.e(TAG, "Failed to get the service label safely, recreating wallet client", e); + mController.reCreateWalletClient(); + try { + return mController.getWalletClient().getServiceLabel(); + } catch (RuntimeException e2) { + Log.e(TAG, "The QAW service label is broken.", e2); + return null; + } + } + } + @Override protected void handleUpdateState(State state, Object arg) { - CharSequence label = mController.getWalletClient().getServiceLabel(); + CharSequence label = getServiceLabelSafe(); state.label = label == null ? mLabel : label; state.contentDescription = state.label; Drawable tileIcon = mController.getWalletClient().getTileIcon(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java index a9723341e787..04437ea14bb3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java @@ -146,7 +146,7 @@ public class UserDetailView extends PseudoGridView { @Override public void onClick(View view) { - if (mFalsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)) { + if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java index 51cc32ad39c1..356f67e7ea00 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java @@ -24,7 +24,6 @@ import android.graphics.Region; import android.graphics.RenderNode; import android.graphics.drawable.Drawable; import android.os.Handler; -import android.util.Log; import androidx.annotation.UiThread; @@ -140,7 +139,6 @@ class ImageTileSet { * getHeight()). */ Bitmap toBitmap(Rect bounds) { - Log.d(TAG, "exporting with bounds: " + bounds); if (mTiles.isEmpty()) { return null; } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index af0141c81d58..0eaef72ae29b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -63,7 +63,7 @@ import javax.inject.Inject; * and bottom before saving/sharing/editing. */ public class LongScreenshotActivity extends Activity { - private static final String TAG = "LongScreenshotActivity"; + private static final String TAG = LogConfig.logTag(LongScreenshotActivity.class); public static final String EXTRA_CAPTURE_RESPONSE = "capture-response"; private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path"; @@ -80,7 +80,6 @@ public class LongScreenshotActivity extends Activity { private View mSave; private View mEdit; private View mShare; - private View mDelete; private CropView mCropView; private MagnifierView mMagnifierView; private ScrollCaptureResponse mScrollCaptureResponse; @@ -113,7 +112,6 @@ public class LongScreenshotActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { - Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")"); super.onCreate(savedInstanceState); setContentView(R.layout.long_screenshot); @@ -121,24 +119,18 @@ public class LongScreenshotActivity extends Activity { mSave = requireViewById(R.id.save); mEdit = requireViewById(R.id.edit); mShare = requireViewById(R.id.share); - mDelete = requireViewById(R.id.delete); mCropView = requireViewById(R.id.crop_view); mMagnifierView = requireViewById(R.id.magnifier); mCropView.setCropInteractionListener(mMagnifierView); mTransitionView = requireViewById(R.id.transition); mEnterTransitionView = requireViewById(R.id.enter_transition); + requireViewById(R.id.cancel).setOnClickListener(v -> finishAndRemoveTask()); + mSave.setOnClickListener(this::onClicked); mEdit.setOnClickListener(this::onClicked); mShare.setOnClickListener(this::onClicked); - // Only show the delete button if we have something to delete (should typically be the case) - if (getIntent().getData() != null) { - mDelete.setOnClickListener(this::onClicked); - } else { - mDelete.setVisibility(View.GONE); - } - mPreview.addOnLayoutChangeListener( (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> updateImageDimensions()); @@ -161,9 +153,13 @@ public class LongScreenshotActivity extends Activity { @Override public void onStart() { - Log.d(TAG, "onStart"); super.onStart(); + if (mPreview.getDrawable() != null) { + // We already have an image, so no need to try to load again. + return; + } + if (mCacheLoadFuture != null) { Log.d(TAG, "mCacheLoadFuture != null"); final ListenableFuture<ImageLoader.Result> future = mCacheLoadFuture; @@ -194,7 +190,7 @@ public class LongScreenshotActivity extends Activity { } private void onLongScreenshotReceived(LongScreenshot longScreenshot) { - Log.d(TAG, "onLongScreenshotReceived(longScreenshot=" + longScreenshot + ")"); + Log.i(TAG, "Completed: " + longScreenshot); mLongScreenshot = longScreenshot; Drawable drawable = mLongScreenshot.getDrawable(); mPreview.setImageDrawable(drawable); @@ -249,7 +245,6 @@ public class LongScreenshotActivity extends Activity { } private void onCachedImageLoaded(ImageLoader.Result imageResult) { - Log.d(TAG, "onCachedImageLoaded(imageResult=" + imageResult + ")"); BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap); mPreview.setImageDrawable(drawable); mPreview.setAlpha(1f); @@ -274,7 +269,6 @@ public class LongScreenshotActivity extends Activity { @Override protected void onSaveInstanceState(Bundle outState) { - Log.d(TAG, "onSaveInstanceState"); super.onSaveInstanceState(outState); if (mSavedImagePath != null) { outState.putString(KEY_SAVED_IMAGE_PATH, mSavedImagePath.getPath()); @@ -282,14 +276,7 @@ public class LongScreenshotActivity extends Activity { } @Override - protected void onPause() { - Log.d(TAG, "onPause"); - super.onPause(); - } - - @Override protected void onStop() { - Log.d(TAG, "onStop finishing=" + isFinishing()); super.onStop(); if (mTransitionStarted) { finish(); @@ -311,24 +298,16 @@ public class LongScreenshotActivity extends Activity { mCacheSaveFuture.cancel(true); } if (mSavedImagePath != null) { - Log.d(TAG, "Deleting " + mSavedImagePath); //noinspection ResultOfMethodCallIgnored mSavedImagePath.delete(); mSavedImagePath = null; } } - @Override - protected void onDestroy() { - Log.d(TAG, "onDestroy"); - super.onDestroy(); - } - private void setButtonsEnabled(boolean enabled) { mSave.setEnabled(enabled); mEdit.setEnabled(enabled); mShare.setEnabled(enabled); - mDelete.setEnabled(enabled); } private void doEdit(Uri uri) { @@ -383,16 +362,10 @@ public class LongScreenshotActivity extends Activity { } else if (id == R.id.share) { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE); startExport(PendingAction.SHARE); - } else if (id == R.id.delete) { - mBackgroundExecutor.execute(() -> { - getContentResolver().delete(getIntent().getData(), null); - finishAndRemoveTask(); - }); } } private void startExport(PendingAction action) { - Log.d(TAG, "startExport(action = " + action + ")"); Drawable drawable = mPreview.getDrawable(); if (drawable == null) { Log.e(TAG, "No drawable, skipping export!"); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 52b393f563b6..16872b08b9c8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -264,7 +264,6 @@ public class ScreenshotController { private Animator mScreenshotAnimation; private RequestCallback mCurrentRequestCallback; - private Uri mLatestUriSaved; private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) { @Override @@ -547,6 +546,7 @@ public class ScreenshotController { mAccessibilityManager.sendAccessibilityEvent(event); } + if (mScreenshotView.isAttachedToWindow()) { // if we didn't already dismiss for another reason if (!mScreenshotView.isDismissing()) { @@ -563,7 +563,6 @@ public class ScreenshotController { .getWindowInsets().getDisplayCutout()); mScreenBitmap = screenshot; - mLatestUriSaved = null; if (!isUserSetupComplete()) { Log.w(TAG, "User setup not complete, displaying toast only"); @@ -701,7 +700,6 @@ public class ScreenshotController { longScreenshot)); final Intent intent = new Intent(mContext, LongScreenshotActivity.class); - intent.setData(mLatestUriSaved); intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -891,8 +889,6 @@ public class ScreenshotController { resetTimeout(); - mLatestUriSaved = imageData.uri; - if (imageData.uri != null) { mScreenshotHandler.post(() -> { if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 4c1f6a19b96c..6dc68746e3ec 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -43,7 +43,7 @@ import javax.inject.Inject; * Interaction controller between the UI and ScrollCaptureClient. */ public class ScrollCaptureController { - private static final String TAG = "ScrollCaptureController"; + private static final String TAG = LogConfig.logTag(ScrollCaptureController.class); private static final float MAX_PAGES_DEFAULT = 3f; private static final String SETTING_KEY_MAX_PAGES = "screenshot.scroll_max_pages"; @@ -90,7 +90,9 @@ public class ScrollCaptureController { /** Releases image resources from the screenshot. */ public void release() { - Log.d(TAG, "LongScreenshot :: release()"); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "LongScreenshot :: release()"); + } mImageTileSet.clear(); mSession.release(); } @@ -153,15 +155,11 @@ public class ScrollCaptureController { * @return a future ImageTile set containing the result */ ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) { - Log.d(TAG, "run: " + response); return CallbackToFutureAdapter.getFuture(completer -> { - Log.d(TAG, "getFuture(ImageTileSet) "); mCaptureCompleter = completer; mBgExecutor.execute(() -> { - Log.d(TAG, "bgExecutor.execute"); float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(), SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT); - Log.d(TAG, "client start, maxPages=" + maxPages); mSessionFuture = mClient.start(response, maxPages); mSessionFuture.addListener(this::onStartComplete, mContext.getMainExecutor()); }); @@ -172,21 +170,27 @@ public class ScrollCaptureController { private void onStartComplete() { try { mSession = mSessionFuture.get(); - Log.d(TAG, "got session " + mSession); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "got session " + mSession); + } requestNextTile(0); } catch (InterruptedException | ExecutionException e) { // Failure to start, propagate to caller - Log.d(TAG, "session start failed!"); + Log.e(TAG, "session start failed!"); mCaptureCompleter.setException(e); } } private void requestNextTile(int topPx) { - Log.d(TAG, "requestNextTile: " + topPx); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "requestNextTile: " + topPx); + } mTileFuture = mSession.requestTile(topPx); mTileFuture.addListener(() -> { try { - Log.d(TAG, "onCaptureResult"); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "onCaptureResult"); + } onCaptureResult(mTileFuture.get()); } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "requestTile failed!", e); @@ -196,14 +200,18 @@ public class ScrollCaptureController { } private void onCaptureResult(CaptureResult result) { - Log.d(TAG, "onCaptureResult: " + result + " scrolling " + (mScrollingUp ? "UP" : "DOWN") - + " finish on boundary: " + mFinishOnBoundary); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "onCaptureResult: " + result + " scrolling " + (mScrollingUp ? "UP" : "DOWN") + + " finish on boundary: " + mFinishOnBoundary); + } boolean emptyResult = result.captured.height() == 0; if (emptyResult) { // Potentially reached a vertical boundary. Extend in the other direction. if (mFinishOnBoundary) { - Log.d(TAG, "Empty: finished!"); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "Empty: finished!"); + } finishCapture(); return; } else { @@ -212,13 +220,17 @@ public class ScrollCaptureController { mImageTileSet.clear(); mFinishOnBoundary = true; mScrollingUp = !mScrollingUp; - Log.d(TAG, "Empty: cleared, switch direction to finish"); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "Empty: cleared, switch direction to finish"); + } } } else { // Got a non-empty result, but may already have enough bitmap data now int expectedTiles = mImageTileSet.size() + 1; if (expectedTiles >= mSession.getMaxTiles()) { - Log.d(TAG, "Hit max tiles: finished"); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "Hit max tiles: finished"); + } // If we ever hit the max tiles, we've got enough bitmap data to finish // (even if we weren't sure we'd finish on this pass). finishCapture(); @@ -229,7 +241,9 @@ public class ScrollCaptureController { // by IDEAL_PORTION_ABOVE. if (mImageTileSet.getHeight() + result.captured.height() >= mSession.getTargetHeight() * IDEAL_PORTION_ABOVE) { - Log.d(TAG, "Hit ideal portion above: clear and switch direction"); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "Hit ideal portion above: clear and switch direction"); + } // We got enough above the start point, now see how far down it can go. mImageTileSet.clear(); mScrollingUp = false; @@ -241,20 +255,25 @@ public class ScrollCaptureController { if (!emptyResult) { mImageTileSet.addTile(new ImageTile(result.image, result.captured)); } - - Log.d(TAG, "bounds: " + mImageTileSet.getLeft() + "," + mImageTileSet.getTop() - + " - " + mImageTileSet.getRight() + "," + mImageTileSet.getBottom() - + " (" + mImageTileSet.getWidth() + "x" + mImageTileSet.getHeight() + ")"); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "bounds: " + mImageTileSet.getLeft() + "," + mImageTileSet.getTop() + + " - " + mImageTileSet.getRight() + "," + mImageTileSet.getBottom() + + " (" + mImageTileSet.getWidth() + "x" + mImageTileSet.getHeight() + ")"); + } Rect gapBounds = mImageTileSet.getGaps(); if (!gapBounds.isEmpty()) { - Log.d(TAG, "Found gaps in tileset: " + gapBounds + ", requesting " + gapBounds.top); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "Found gaps in tileset: " + gapBounds + ", requesting " + gapBounds.top); + } requestNextTile(gapBounds.top); return; } if (mImageTileSet.getHeight() >= mSession.getTargetHeight()) { - Log.d(TAG, "Target height reached."); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "Target height reached."); + } finishCapture(); return; } @@ -275,10 +294,14 @@ public class ScrollCaptureController { } private void finishCapture() { - Log.d(TAG, "finishCapture()"); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "finishCapture()"); + } mEndFuture = mSession.end(); mEndFuture.addListener(() -> { - Log.d(TAG, "endCapture completed"); + if (LogConfig.DEBUG_SCROLL) { + Log.d(TAG, "endCapture completed"); + } // Provide result to caller and complete the top-level future // Caller is responsible for releasing this resource (ImageReader/HardwareBuffers) mCaptureCompleter.set(new LongScreenshot(mSession, mImageTileSet)); diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt index 24775344240a..f0fb5ebf9e1d 100644 --- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt @@ -17,6 +17,7 @@ package com.android.systemui.sensorprivacy import android.content.DialogInterface +import android.content.Intent import android.content.Intent.EXTRA_PACKAGE_NAME import android.content.pm.PackageManager import android.content.res.Resources @@ -38,6 +39,10 @@ import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject +import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION +import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE +import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL +import com.android.internal.util.FrameworkStatsLog.write /** * Dialog to be shown on top of apps that are attempting to use a sensor (e.g. microphone) which is @@ -174,7 +179,7 @@ class SensorUseStartedActivity @Inject constructor( override fun onStart() { super.onStart() - sensorPrivacyController.suppressSensorPrivacyReminders(sensorUsePackageName, true) + setSuppressed(true) unsuppressImmediately = false } @@ -185,16 +190,25 @@ class SensorUseStartedActivity @Inject constructor( keyguardDismissUtil.executeWhenUnlocked({ bgHandler.postDelayed({ disableSensorPrivacy() + write(PRIVACY_TOGGLE_DIALOG_INTERACTION, + PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE, + sensorUsePackageName) }, UNLOCK_DELAY_MILLIS) false }, false, true) } else { disableSensorPrivacy() + write(PRIVACY_TOGGLE_DIALOG_INTERACTION, + PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE, + sensorUsePackageName) } } BUTTON_NEGATIVE -> { unsuppressImmediately = false + write(PRIVACY_TOGGLE_DIALOG_INTERACTION, + PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL, + sensorUsePackageName) } } @@ -205,12 +219,10 @@ class SensorUseStartedActivity @Inject constructor( super.onStop() if (unsuppressImmediately) { - sensorPrivacyController - .suppressSensorPrivacyReminders(sensorUsePackageName, false) + setSuppressed(false) } else { bgHandler.postDelayed({ - sensorPrivacyController - .suppressSensorPrivacyReminders(sensorUsePackageName, false) + setSuppressed(false) }, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS) } } @@ -224,6 +236,11 @@ class SensorUseStartedActivity @Inject constructor( // do not allow backing out } + override fun onNewIntent(intent: Intent?) { + setIntent(intent) + recreate() + } + private fun disableSensorPrivacy() { if (sensor == ALL_SENSORS) { sensorPrivacyController.setSensorBlocked(DIALOG, MICROPHONE, false) @@ -234,4 +251,16 @@ class SensorUseStartedActivity @Inject constructor( unsuppressImmediately = true setResult(RESULT_OK) } + + private fun setSuppressed(suppressed: Boolean) { + if (sensor == ALL_SENSORS) { + sensorPrivacyController + .suppressSensorPrivacyReminders(MICROPHONE, suppressed) + sensorPrivacyController + .suppressSensorPrivacyReminders(CAMERA, suppressed) + } else { + sensorPrivacyController + .suppressSensorPrivacyReminders(sensor, suppressed) + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index baac2549055f..cd5cce4f3ee7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -367,6 +367,7 @@ public class NotificationShelf extends ActivatableNotificationView implements && !mHostLayoutController.isViewAffectedBySwipe(anv) && !isUnlockedHeadsUp && !isHunGoingToShade + && !anv.isAboveShelf() && !mAmbientState.isPulsing() && !mAmbientState.isDozing(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index a00d01427ebc..5302188ccb31 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -416,10 +416,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi return mIcon.icon; } - private Drawable getIcon(StatusBarIcon icon) { - Context notifContext = mNotification != null ? - mNotification.getPackageContext(getContext()) : getContext(); - return getIcon(getContext(), notifContext, icon); + Drawable getIcon(StatusBarIcon icon) { + Context notifContext = getContext(); + if (mNotification != null) { + notifContext = mNotification.getPackageContext(getContext()); + } + return getIcon(getContext(), notifContext != null ? notifContext : getContext(), icon); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index a32b7e3b2836..6964838e7e41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -70,6 +70,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -93,6 +94,10 @@ public interface NotificationsModule { StackScrollAlgorithm.SectionProvider bindSectionProvider( NotificationSectionsManager impl); + @Binds + StackScrollAlgorithm.BypassController bindBypassController( + KeyguardBypassController impl); + /** Provides an instance of {@link NotificationEntryManager} */ @SysUISingleton @Provides diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java index 222735aeb35a..4c9c2f95b35c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java @@ -47,10 +47,6 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { public void onContentUpdated(ExpandableNotificationRow row) { super.onContentUpdated(row); - // Custom views will most likely use just white or black as their text color. - // We need to scan through and replace these colors by Material NEXT colors. - ensureThemeOnChildren(mView); - // Let's invert the notification colors when we're in night mode and // the notification background isn't colorized. if (needsInversion(mBackgroundColor, mView)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java index d21ae13a1e01..8c6fa023d92b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java @@ -62,10 +62,6 @@ public class NotificationDecoratedCustomViewWrapper extends NotificationTemplate public void onContentUpdated(ExpandableNotificationRow row) { mWrappedView = getWrappedCustomView(mView); - // Custom views will most likely use just white or black as their text color. - // We need to scan through and replace these colors by Material NEXT colors. - ensureThemeOnChildren(mWrappedView); - if (needsInversion(resolveBackgroundColor(), mWrappedView)) { invertViewLuminosity(mWrappedView); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 74abd38ec398..76301917b458 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -30,7 +30,6 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.Pair; -import android.view.ContextThemeWrapper; import android.view.NotificationHeaderView; import android.view.View; import android.view.ViewGroup; @@ -41,7 +40,6 @@ import com.android.internal.graphics.ColorUtils; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.settingslib.Utils; -import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.TransformState; @@ -58,11 +56,6 @@ public abstract class NotificationViewWrapper implements TransformableView { private final Rect mTmpRect = new Rect(); protected int mBackgroundColor = 0; - private int mMaterialTextColorPrimary; - private int mMaterialTextColorSecondary; - private int mThemedTextColorPrimary; - private int mThemedTextColorSecondary; - private boolean mAdjustTheme; public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) { if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) { @@ -98,8 +91,6 @@ public abstract class NotificationViewWrapper implements TransformableView { mView = view; mRow = row; onReinflated(); - mAdjustTheme = ctx.getResources().getBoolean( - R.bool.config_adjustThemeOnNotificationCustomViews); } /** @@ -124,22 +115,6 @@ public abstract class NotificationViewWrapper implements TransformableView { mBackgroundColor = backgroundColor; mView.setBackground(new ColorDrawable(Color.TRANSPARENT)); } - - Context materialTitleContext = new ContextThemeWrapper(mView.getContext(), - com.android.internal.R.style.TextAppearance_Material_Notification_Title); - mMaterialTextColorPrimary = Utils.getColorAttr(materialTitleContext, - com.android.internal.R.attr.textColor).getDefaultColor(); - Context materialContext = new ContextThemeWrapper(mView.getContext(), - com.android.internal.R.style.TextAppearance_Material_Notification); - mMaterialTextColorSecondary = Utils.getColorAttr(materialContext, - com.android.internal.R.attr.textColor).getDefaultColor(); - - Context themedContext = new ContextThemeWrapper(mView.getContext(), - com.android.internal.R.style.Theme_DeviceDefault_DayNight); - mThemedTextColorPrimary = Utils.getColorAttr(themedContext, - com.android.internal.R.attr.textColorPrimary).getDefaultColor(); - mThemedTextColorSecondary = Utils.getColorAttr(themedContext, - com.android.internal.R.attr.textColorSecondary).getDefaultColor(); } protected boolean needsInversion(int defaultBackgroundColor, View view) { @@ -217,39 +192,6 @@ public abstract class NotificationViewWrapper implements TransformableView { return false; } - protected void ensureThemeOnChildren(View rootView) { - if (!mAdjustTheme || mView == null || rootView == null) { - return; - } - - // Notifications with custom backgrounds should not be adjusted - if (mBackgroundColor != Color.TRANSPARENT - || getBackgroundColor(mView) != Color.TRANSPARENT - || getBackgroundColor(rootView) != Color.TRANSPARENT) { - return; - } - - // Now let's check if there's unprotected text somewhere, and apply the theme if we find it. - processTextColorRecursive(rootView); - } - - private void processTextColorRecursive(View view) { - if (view instanceof TextView) { - TextView textView = (TextView) view; - int foreground = textView.getCurrentTextColor(); - if (foreground == mMaterialTextColorPrimary) { - textView.setTextColor(mThemedTextColorPrimary); - } else if (foreground == mMaterialTextColorSecondary) { - textView.setTextColor(mThemedTextColorSecondary); - } - } else if (view instanceof ViewGroup) { - ViewGroup viewGroup = (ViewGroup) view; - for (int i = 0; i < viewGroup.getChildCount(); i++) { - processTextColorRecursive(viewGroup.getChildAt(i)); - } - } - } - protected int getBackgroundColor(View view) { if (view == null) { return Color.TRANSPARENT; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 197920f7be19..9846b28ba5f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; +import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider; import javax.inject.Inject; @@ -43,6 +44,7 @@ public class AmbientState { private static final boolean NOTIFICATIONS_HAVE_SHADOWS = false; private final SectionProvider mSectionProvider; + private final BypassController mBypassController; private int mScrollY; private boolean mDimmed; private ActivatableNotificationView mActivatedChild; @@ -152,14 +154,25 @@ public class AmbientState { return mStackHeight; } + /** + * @return Height of notifications panel, with the animation from pulseHeight accounted for. + */ + // TODO(b/192348384): move this logic to getStackHeight, and remove this and getInnerHeight + public float getPulseStackHeight() { + float pulseHeight = Math.min(mPulseHeight, mStackHeight); + return MathUtils.lerp(mStackHeight, pulseHeight, mDozeAmount); + } + /** Tracks the state from AlertingNotificationManager#hasNotifications() */ private boolean mHasAlertEntries; @Inject public AmbientState( Context context, - @NonNull SectionProvider sectionProvider) { + @NonNull SectionProvider sectionProvider, + @NonNull BypassController bypassController) { mSectionProvider = sectionProvider; + mBypassController = bypassController; reload(context); } @@ -297,6 +310,13 @@ public class AmbientState { } } + /** + * Is bypass currently enabled? + */ + public boolean isBypassEnabled() { + return mBypassController.isBypassEnabled(); + } + public float getOverScrollAmount(boolean top) { return top ? mOverScrollTopAmount : mOverScrollBottomAmount; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 3fc8b8d9aef1..f460a132d65c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -148,8 +148,22 @@ public class StackScrollAlgorithm { AmbientState ambientState) { NotificationShelf shelf = ambientState.getShelf(); - if (shelf != null) { - shelf.updateState(algorithmState, ambientState); + if (shelf == null) { + return; + } + + shelf.updateState(algorithmState, ambientState); + + // After the shelf has updated its yTranslation, + // explicitly hide views below the shelf to skip rendering them in the hardware layer. + final float shelfTop = shelf.getViewState().yTranslation; + + for (ExpandableView view : algorithmState.visibleChildren) { + final float viewTop = view.getViewState().yTranslation; + + if (viewTop >= shelfTop) { + view.getViewState().hidden = true; + } } } @@ -411,8 +425,7 @@ public class StackScrollAlgorithm { } else { if (view != ambientState.getTrackedHeadsUpRow()) { if (ambientState.isExpansionChanging()) { - // Show all views. Views below the shelf will later be clipped (essentially - // hidden) in NotificationShelf. + // We later update shelf state, then hide views below the shelf. viewState.hidden = false; viewState.inShelf = algorithmState.firstViewInShelf != null && i >= algorithmState.visibleChildren.indexOf( @@ -421,10 +434,20 @@ public class StackScrollAlgorithm { // When pulsing (incoming notification on AOD), innerHeight is 0; clamp all // to shelf start, thereby hiding all notifications (except the first one, which // we later unhide in updatePulsingState) - final int stackBottom = - !ambientState.isShadeExpanded() || ambientState.isDozing() - ? ambientState.getInnerHeight() - : (int) ambientState.getStackHeight(); + // TODO(b/192348384): merge InnerHeight with StackHeight + final int stackBottom; + if (ambientState.isBypassEnabled()) { + // We want to use the stackHeight when pulse expanding, since the animation + // isn't currently optimized if the pulseHeight is continuously changing + // Let's improve this when we're merging the heights above + stackBottom = ambientState.isPulseExpanding() + ? (int) ambientState.getStackHeight() + : ambientState.getInnerHeight(); + } else { + stackBottom = !ambientState.isShadeExpanded() || ambientState.isDozing() + ? ambientState.getInnerHeight() + : (int) ambientState.getPulseStackHeight(); + } final int shelfStart = stackBottom - ambientState.getShelf().getIntrinsicHeight(); viewState.yTranslation = Math.min(viewState.yTranslation, shelfStart); @@ -742,4 +765,14 @@ public class StackScrollAlgorithm { */ boolean beginsSection(@NonNull View view, @Nullable View previous); } + + /** + * Interface for telling the StackScrollAlgorithm information about the bypass state + */ + public interface BypassController { + /** + * True if bypass is enabled. Note that this is always false if face auth is not enabled. + */ + boolean isBypassEnabled(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 917c79fc6de5..6d5c53609f81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -39,6 +39,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; import com.android.systemui.Dumpable; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; @@ -165,6 +166,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private BiometricModeListener mBiometricModeListener; private final MetricsLogger mMetricsLogger; + private final AuthController mAuthController; private static final class PendingAuthenticated { public final int userId; @@ -254,7 +256,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp PowerManager powerManager, NotificationMediaManager notificationMediaManager, WakefulnessLifecycle wakefulnessLifecycle, - ScreenLifecycle screenLifecycle) { + ScreenLifecycle screenLifecycle, + AuthController authController) { mContext = context; mPowerManager = powerManager; mShadeController = shadeController; @@ -275,6 +278,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mKeyguardBypassController = keyguardBypassController; mKeyguardBypassController.setUnlockController(this); mMetricsLogger = metricsLogger; + mAuthController = authController; dumpManager.registerDumpable(getClass().getName(), this); } @@ -596,7 +600,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp return MODE_DISMISS_BOUNCER; } } else if (unlockingAllowed) { - return bypass ? MODE_UNLOCK_FADING : MODE_NONE; + return bypass || mAuthController.isUdfpsFingerDown() + ? MODE_UNLOCK_FADING : MODE_NONE; } else { return bypass ? MODE_SHOW_BOUNCER : MODE_NONE; } 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 077500f7bfb9..f6c475690c1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -75,6 +75,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.EmergencyCarrierArea; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.Utils; import com.android.systemui.ActivityIntentHelper; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -463,6 +464,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL if (tileIcon != null) { mWalletButton.setImageDrawable(tileIcon); } + mWalletButton.getDrawable().setTint( + Utils.getColorAttr( + mContext, + com.android.internal.R.attr.textColorPrimary).getDefaultColor()); mWalletButton.setVisibility(VISIBLE); mWalletButton.setOnClickListener(this::onWalletClick); mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index d6ea4a828395..8c0dfc5f7ab4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -87,7 +87,7 @@ public class KeyguardBouncer { private final Runnable mResetRunnable = ()-> { if (mKeyguardViewController != null) { mKeyguardViewController.resetSecurityContainer(); - for (KeyguardResetCallback callback : mResetCallbacks) { + for (KeyguardResetCallback callback : new ArrayList<>(mResetCallbacks)) { callback.onKeyguardReset(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index c9d08427c8ca..2cb0a3a28901 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -28,6 +28,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.tuner.TunerService import java.io.FileDescriptor @@ -35,7 +36,7 @@ import java.io.PrintWriter import javax.inject.Inject @SysUISingleton -open class KeyguardBypassController : Dumpable { +open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassController { private val mKeyguardStateController: KeyguardStateController private val statusBarStateController: StatusBarStateController @@ -67,6 +68,9 @@ open class KeyguardBypassController : Dumpable { lateinit var unlockController: BiometricUnlockController var isPulseExpanding = false + /** delegates to [bypassEnabled] but conforms to [StackScrollAlgorithm.BypassController] */ + override fun isBypassEnabled() = bypassEnabled + /** * If face unlock dismisses the lock screen or keeps user on keyguard for the current user. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index c89f698f2656..fb3790cf4599 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -115,6 +115,7 @@ import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.qs.QSDetailDisplayer; +import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FeatureFlags; @@ -329,6 +330,7 @@ public class NotificationPanelViewController extends PanelViewController { private final int mMaxKeyguardNotifications; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final TapAgainViewController mTapAgainViewController; + private final RecordingController mRecordingController; private boolean mShouldUseSplitNotificationShade; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; @@ -716,6 +718,7 @@ public class NotificationPanelViewController extends PanelViewController { FragmentService fragmentService, ContentResolver contentResolver, QuickAccessWalletController quickAccessWalletController, + RecordingController recordingController, @Main Executor uiExecutor, SecureSettings secureSettings, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, @@ -762,6 +765,7 @@ public class NotificationPanelViewController extends PanelViewController { mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); setPanelAlpha(255, false /* animate */); mCommandQueue = commandQueue; + mRecordingController = recordingController; mDisplayId = displayId; mPulseExpansionHandler = pulseExpansionHandler; mDozeParameters = dozeParameters; @@ -1361,7 +1365,8 @@ public class NotificationPanelViewController extends PanelViewController { float lockIconPadding = 0; if (mLockIconViewController.getTop() != 0) { - lockIconPadding = mStatusBar.getDisplayHeight() - mLockIconViewController.getTop(); + lockIconPadding = mStatusBar.getDisplayHeight() - mLockIconViewController.getTop() + + mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding); } float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); @@ -1581,7 +1586,10 @@ public class NotificationPanelViewController extends PanelViewController { public void expandWithQsDetail(DetailAdapter qsDetailAdapter) { traceQsJank(true /* startTracing */, false /* wasCancelled */); flingSettings(0 /* velocity */, FLING_EXPAND); - mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, 0, 0); + // When expanding with a panel, there's no meaningful touch point to correspond to. Set the + // origin to somewhere above the screen. This is used for animations. + int x = mQsFrame.getWidth() / 2; + mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, x, -getHeight()); if (mAccessibilityManager.isEnabled()) { mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); } @@ -1636,7 +1644,6 @@ public class NotificationPanelViewController extends PanelViewController { mView.getParent().requestDisallowInterceptTouchEvent(true); } if (mQsExpansionAnimator != null) { - onQsExpansionStarted(); mInitialHeightOnTouch = mQsExpansionHeight; mQsTracking = true; traceQsJank(true /* startTracing */, false /* wasCancelled */); @@ -2370,7 +2377,8 @@ public class NotificationPanelViewController extends PanelViewController { // The padding on this area is large enough that we can use a cheaper clipping strategy mKeyguardStatusAreaClipBounds.set(left, top, right, bottom); clipStatusView = qsVisible; - radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius, + float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius; + radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius, Math.min(top / (float) mScrimCornerRadius, 1f)); statusBarClipTop = top - mKeyguardStatusBar.getTop(); } @@ -3638,6 +3646,8 @@ public class NotificationPanelViewController extends PanelViewController { public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) { if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) { mAmbientIndicationBottomPadding = ambientIndicationBottomPadding; + mLockIconViewController.setAmbientIndicationBottomPadding( + mAmbientIndicationBottomPadding); updateMaxDisplayedNotifications(true); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index 022faf78b946..5e105bb64350 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -55,16 +55,18 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.google.android.collect.Lists; - import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashSet; +import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; import javax.inject.Inject; @@ -100,7 +102,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW private ForcePluginOpenListener mForcePluginOpenListener; private Consumer<Integer> mScrimsVisibilityListener; private final ArrayList<WeakReference<StatusBarWindowCallback>> - mCallbacks = Lists.newArrayList(); + mCallbacks = new ArrayList<>(); private final SysuiColorExtractor mColorExtractor; private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; @@ -464,13 +466,15 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW @Override public void notifyStateChangedCallbacks() { - for (int i = 0; i < mCallbacks.size(); i++) { - StatusBarWindowCallback cb = mCallbacks.get(i).get(); - if (cb != null) { - cb.onStateChanged(mCurrentState.mKeyguardShowing, - mCurrentState.mKeyguardOccluded, - mCurrentState.mBouncerShowing); - } + // Copy callbacks to separate ArrayList to avoid concurrent modification + List<StatusBarWindowCallback> activeCallbacks = mCallbacks.stream() + .map(Reference::get) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + for (StatusBarWindowCallback cb : activeCallbacks) { + cb.onStateChanged(mCurrentState.mKeyguardShowing, + mCurrentState.mKeyguardOccluded, + mCurrentState.mBouncerShowing); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index dc45bf1d324e..234df9ef48f7 100755 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -109,7 +109,8 @@ public class PhoneStatusBarPolicy private final String mSlotBluetooth; private final String mSlotTty; private final String mSlotZen; - private final String mSlotVolume; + private final String mSlotMute; + private final String mSlotVibrate; private final String mSlotAlarmClock; private final String mSlotManagedProfile; private final String mSlotRotate; @@ -151,7 +152,8 @@ public class PhoneStatusBarPolicy private final PrivacyLogger mPrivacyLogger; private boolean mZenVisible; - private boolean mVolumeVisible; + private boolean mVibrateVisible; + private boolean mMuteVisible; private boolean mCurrentUserSetup; private boolean mManagedProfileIconVisible = false; @@ -209,7 +211,8 @@ public class PhoneStatusBarPolicy mSlotBluetooth = resources.getString(com.android.internal.R.string.status_bar_bluetooth); mSlotTty = resources.getString(com.android.internal.R.string.status_bar_tty); mSlotZen = resources.getString(com.android.internal.R.string.status_bar_zen); - mSlotVolume = resources.getString(com.android.internal.R.string.status_bar_volume); + mSlotMute = resources.getString(com.android.internal.R.string.status_bar_mute); + mSlotVibrate = resources.getString(com.android.internal.R.string.status_bar_volume); mSlotAlarmClock = resources.getString(com.android.internal.R.string.status_bar_alarm_clock); mSlotManagedProfile = resources.getString( com.android.internal.R.string.status_bar_managed_profile); @@ -266,9 +269,14 @@ public class PhoneStatusBarPolicy mIconController.setIcon(mSlotZen, R.drawable.stat_sys_dnd, null); mIconController.setIconVisibility(mSlotZen, false); - // volume - mIconController.setIcon(mSlotVolume, R.drawable.stat_sys_ringer_vibrate, null); - mIconController.setIconVisibility(mSlotVolume, false); + // vibrate + mIconController.setIcon(mSlotVibrate, R.drawable.stat_sys_ringer_vibrate, + mResources.getString(R.string.accessibility_ringer_vibrate)); + mIconController.setIconVisibility(mSlotVibrate, false); + // mute + mIconController.setIcon(mSlotMute, R.drawable.stat_sys_ringer_silent, + mResources.getString(R.string.accessibility_ringer_silent)); + mIconController.setIconVisibility(mSlotMute, false); updateVolumeZen(); // cast @@ -369,9 +377,8 @@ public class PhoneStatusBarPolicy int zenIconId = 0; String zenDescription = null; - boolean volumeVisible = false; - int volumeIconId = 0; - String volumeDescription = null; + boolean vibrateVisible = false; + boolean muteVisible = false; int zen = mZenController.getZen(); if (DndTile.isVisible(mSharedPreferences) || DndTile.isCombinedIcon(mSharedPreferences)) { @@ -393,13 +400,9 @@ public class PhoneStatusBarPolicy mRingerModeTracker.getRingerModeInternal().getValue(); if (ringerModeInternal != null) { if (ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) { - volumeVisible = true; - volumeIconId = R.drawable.stat_sys_ringer_vibrate; - volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate); + vibrateVisible = true; } else if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) { - volumeVisible = true; - volumeIconId = R.drawable.stat_sys_ringer_silent; - volumeDescription = mResources.getString(R.string.accessibility_ringer_silent); + muteVisible = true; } } } @@ -412,13 +415,16 @@ public class PhoneStatusBarPolicy mZenVisible = zenVisible; } - if (volumeVisible) { - mIconController.setIcon(mSlotVolume, volumeIconId, volumeDescription); + if (vibrateVisible != mVibrateVisible) { + mIconController.setIconVisibility(mSlotVibrate, vibrateVisible); + mVibrateVisible = vibrateVisible; } - if (volumeVisible != mVolumeVisible) { - mIconController.setIconVisibility(mSlotVolume, volumeVisible); - mVolumeVisible = volumeVisible; + + if (muteVisible != mMuteVisible) { + mIconController.setIconVisibility(mSlotMute, muteVisible); + mMuteVisible = muteVisible; } + updateAlarm(); } 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 8eb22c63bbcf..c68429a56225 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -247,6 +247,7 @@ import com.android.systemui.volume.VolumeComponent; import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.startingsurface.StartingSurface; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -685,9 +686,7 @@ public class StatusBar extends SystemUI implements DemoMode, new FalsingManager.FalsingBeliefListener() { @Override public void onFalse() { - // Hides quick settings. - mNotificationPanelViewController.resetViews(true); - // Hides bouncer and quick-quick settings. + // Hides quick settings, bouncer, and quick-quick settings. mStatusBarKeyguardViewManager.reset(true); } }; @@ -705,6 +704,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final Optional<BubblesManager> mBubblesManagerOptional; private final Optional<Bubbles> mBubblesOptional; private final Bubbles.BubbleExpandListener mBubbleExpandListener; + private final Optional<StartingSurface> mStartingSurfaceOptional; private ActivityIntentHelper mActivityIntentHelper; private NotificationStackScrollLayoutController mStackScrollerController; @@ -804,7 +804,8 @@ public class StatusBar extends SystemUI implements DemoMode, LockscreenShadeTransitionController lockscreenShadeTransitionController, FeatureFlags featureFlags, KeyguardUnlockAnimationController keyguardUnlockAnimationController, - UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) { + UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, + Optional<StartingSurface> startingSurfaceOptional) { super(context); mNotificationsController = notificationsController; mLightBarController = lightBarController; @@ -891,6 +892,7 @@ public class StatusBar extends SystemUI implements DemoMode, mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; + mStartingSurfaceOptional = startingSurfaceOptional; lockscreenShadeTransitionController.setStatusbar(this); mExpansionChangedListeners = new ArrayList<>(); @@ -1419,7 +1421,9 @@ public class StatusBar extends SystemUI implements DemoMode, private void setUpPresenter() { // Set up the initial notification state. - mActivityLaunchAnimator = new ActivityLaunchAnimator(this, mContext); + mActivityLaunchAnimator = new ActivityLaunchAnimator(this, + mStartingSurfaceOptional.orElse(null), + mContext); mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( mNotificationShadeWindowViewController, mStackScrollerController.getNotificationListContainer(), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 6f63b1780d68..8a7708aaa8c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -521,7 +521,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void reset(boolean hideBouncerWhenShowing) { if (mShowing) { - mNotificationPanelViewController.closeQs(); + // Hide quick settings. + mNotificationPanelViewController.resetViews(/* animate= */ true); + // Hide bouncer and quick-quick settings. if (mOccluded && !mDozing) { mStatusBar.hideKeyguard(); if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 98b9cc9bc716..9a6dd38ffca5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -514,7 +514,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON ); ActivityLaunchAnimator.Controller animationController = - new StatusBarLaunchAnimatorController(viewController, mStatusBar, + viewController == null ? null + : new StatusBarLaunchAnimatorController(viewController, mStatusBar, true /* isActivityIntent */); mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 2611ab5f7016..716d1dbc6462 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -109,6 +109,7 @@ import com.android.systemui.volume.VolumeComponent; import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.startingsurface.StartingSurface; import java.util.Optional; import java.util.concurrent.Executor; @@ -218,7 +219,8 @@ public interface StatusBarPhoneModule { LockscreenShadeTransitionController transitionController, FeatureFlags featureFlags, KeyguardUnlockAnimationController keyguardUnlockAnimationController, - UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) { + UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, + Optional<StartingSurface> startingSurfaceOptional) { return new StatusBar( context, notificationsController, @@ -306,6 +308,7 @@ public interface StatusBarPhoneModule { transitionController, featureFlags, keyguardUnlockAnimationController, - unlockedScreenOffAnimationController); + unlockedScreenOffAnimationController, + startingSurfaceOptional); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index 081fe5a47626..a8097c4d74b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -41,6 +41,7 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashSet; /** @@ -157,7 +158,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { NotificationPeekEvent.NOTIFICATION_PEEK, entry.getSbn().getUid(), entry.getSbn().getPackageName(), entry.getSbn().getInstanceId()); } - for (OnHeadsUpChangedListener listener : mListeners) { + for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) { if (isPinned) { listener.onHeadsUpPinned(entry); } else { @@ -177,7 +178,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { entry.setHeadsUp(true); setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry)); EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 1 /* visible */); - for (OnHeadsUpChangedListener listener : mListeners) { + for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) { listener.onHeadsUpStateChanged(entry, true); } } @@ -188,7 +189,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { entry.setHeadsUp(false); setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */); EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 0 /* visible */); - for (OnHeadsUpChangedListener listener : mListeners) { + for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) { listener.onHeadsUpStateChanged(entry, false); } } @@ -206,7 +207,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { if (mHasPinnedNotification) { MetricsLogger.count(mContext, "note_peek", 1); } - for (OnHeadsUpChangedListener listener : mListeners) { + for (OnHeadsUpChangedListener listener : new ArrayList<>(mListeners)) { listener.onHeadsUpPinnedModeChanged(hasPinnedNotification); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java index acfdda4cea49..e01b95e81da8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java @@ -29,7 +29,7 @@ public interface IndividualSensorPrivacyController extends void setSensorBlocked(@Source int source, @Sensor int sensor, boolean blocked); - void suppressSensorPrivacyReminders(String packageName, boolean suppress); + void suppressSensorPrivacyReminders(int sensor, boolean suppress); interface Callback { void onSensorBlockedChanged(@Sensor int sensor, boolean blocked); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java index 9807165f69d0..1d71301c7454 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java @@ -68,8 +68,8 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr } @Override - public void suppressSensorPrivacyReminders(String packageName, boolean suppress) { - mSensorPrivacyManager.suppressSensorPrivacyReminders(packageName, suppress); + public void suppressSensorPrivacyReminders(int sensor, boolean suppress) { + mSensorPrivacyManager.suppressSensorPrivacyReminders(sensor, suppress); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java index 1f1817cd29f0..5e70d0dbc418 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java @@ -21,7 +21,7 @@ import android.content.res.Resources; import android.database.DataSetObserver; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; -import android.os.UserManager; +import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -76,7 +76,6 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie private final KeyguardVisibilityHelper mKeyguardVisibilityHelper; private final KeyguardUserDetailAdapter mUserDetailAdapter; private NotificationPanelViewController mNotificationPanelViewController; - private UserManager mUserManager; UserSwitcherController.UserRecord mCurrentUser; // State info for the user switch and keyguard @@ -115,7 +114,6 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie UserAvatarView view, Context context, @Main Resources resources, - UserManager userManager, ScreenLifecycle screenLifecycle, UserSwitcherController userSwitcherController, KeyguardStateController keyguardStateController, @@ -129,7 +127,6 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie if (DEBUG) Log.d(TAG, "New KeyguardQsUserSwitchController"); mContext = context; mResources = resources; - mUserManager = userManager; mScreenLifecycle = screenLifecycle; mUserSwitcherController = userSwitcherController; mKeyguardStateController = keyguardStateController; @@ -227,47 +224,39 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie return; } - if (mCurrentUser == null) { - mView.setVisibility(View.GONE); - return; - } - - mView.setVisibility(View.VISIBLE); - - String currentUserName = mCurrentUser.info.name; String contentDescription = null; - - if (!TextUtils.isEmpty(currentUserName)) { + if (mCurrentUser != null && mCurrentUser.info != null && !TextUtils.isEmpty( + mCurrentUser.info.name)) { + // If we know the current user's name, have TalkBack to announce "Signed in as [user + // name]" when the icon is selected + contentDescription = mContext.getString(R.string.accessibility_quick_settings_user, + mCurrentUser.info.name); + } else { + // As a fallback, have TalkBack announce "Switch user" contentDescription = mContext.getString( - R.string.accessibility_quick_settings_user, - currentUserName); + R.string.accessibility_multi_user_switch_switcher); } if (!TextUtils.equals(mView.getContentDescription(), contentDescription)) { mView.setContentDescription(contentDescription); } - mView.setDrawableWithBadge(getCurrentUserIcon().mutate(), mCurrentUser.resolveId()); + int userId = mCurrentUser != null ? mCurrentUser.resolveId() : UserHandle.USER_NULL; + mView.setDrawableWithBadge(getCurrentUserIcon().mutate(), userId); } Drawable getCurrentUserIcon() { Drawable drawable; - if (mCurrentUser.picture == null) { - if (mCurrentUser.isCurrent && mCurrentUser.isGuest) { + if (mCurrentUser == null || mCurrentUser.picture == null) { + if (mCurrentUser != null && mCurrentUser.isGuest) { drawable = mContext.getDrawable(R.drawable.ic_avatar_guest_user); } else { - drawable = mAdapter.getIconDrawable(mContext, mCurrentUser); - } - int iconColorRes; - if (mCurrentUser.isSwitchToEnabled) { - iconColorRes = R.color.kg_user_switcher_avatar_icon_color; - } else { - iconColorRes = R.color.kg_user_switcher_restricted_avatar_icon_color; + drawable = mContext.getDrawable(R.drawable.ic_avatar_user); } + int iconColorRes = R.color.kg_user_switcher_avatar_icon_color; drawable.setTint(mResources.getColor(iconColorRes, mContext.getTheme())); } else { - int avatarSize = - (int) mResources.getDimension(R.dimen.kg_framed_avatar_size); + int avatarSize = (int) mResources.getDimension(R.dimen.kg_framed_avatar_size); drawable = new CircleFramedDrawable(mCurrentUser.picture, avatarSize); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 740b52d332ce..9c96416361c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -705,7 +705,7 @@ public class NetworkControllerImpl extends BroadcastReceiver cb.setIsAirplaneMode(new IconState(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext)); cb.setNoSims(mHasNoSubs, mSimDetected); - if (mProviderModelBehavior) { + if (mProviderModelSetting) { cb.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, mNoNetworksAvailable); } mWifiSignalController.notifyListeners(cb); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index b18dfd2866c4..bbaf65a399a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -807,7 +807,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } private void onEditTextFocusChanged(RemoteEditText remoteEditText, boolean focused) { - for (View.OnFocusChangeListener listener : mEditTextFocusChangeListeners) { + for (View.OnFocusChangeListener listener : new ArrayList<>(mEditTextFocusChangeListeners)) { listener.onFocusChange(remoteEditText, focused); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index e2b6895e7039..4e921a036b36 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -67,6 +67,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.qs.tiles.UserDetailView; @@ -131,6 +132,7 @@ public class UserSwitcherController implements Dumpable { private final Executor mUiBgExecutor; private final boolean mGuestUserAutoCreated; private final AtomicBoolean mGuestCreationScheduled; + private FalsingManager mFalsingManager; @Inject public UserSwitcherController(Context context, @@ -139,6 +141,7 @@ public class UserSwitcherController implements Dumpable { ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher, UiEventLogger uiEventLogger, + FalsingManager falsingManager, TelephonyListenerManager telephonyListenerManager, IActivityTaskManager activityTaskManager, UserDetailAdapter userDetailAdapter, @@ -148,6 +151,7 @@ public class UserSwitcherController implements Dumpable { mTelephonyListenerManager = telephonyListenerManager; mActivityTaskManager = activityTaskManager; mUiEventLogger = uiEventLogger; + mFalsingManager = falsingManager; mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(this, mUiEventLogger); mUserDetailAdapter = userDetailAdapter; mUiBgExecutor = uiBgExecutor; @@ -1031,6 +1035,11 @@ public class UserSwitcherController implements Dumpable { @Override public void onClick(DialogInterface dialog, int which) { + int penalty = which == BUTTON_NEGATIVE ? FalsingManager.NO_PENALTY + : FalsingManager.HIGH_PENALTY; + if (mFalsingManager.isFalseTap(penalty)) { + return; + } if (which == BUTTON_NEGATIVE) { cancel(); } else { @@ -1057,6 +1066,11 @@ public class UserSwitcherController implements Dumpable { @Override public void onClick(DialogInterface dialog, int which) { + int penalty = which == BUTTON_NEGATIVE ? FalsingManager.NO_PENALTY + : FalsingManager.MODERATE_PENALTY; + if (mFalsingManager.isFalseTap(penalty)) { + return; + } if (which == BUTTON_NEGATIVE) { cancel(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 11ddbd045cd4..81999b534046 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -450,19 +450,23 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { OverlayIdentifier systemPalette = categoryToPackage.get(OVERLAY_CATEGORY_SYSTEM_PALETTE); if (mIsMonetEnabled && systemPalette != null && systemPalette.getPackageName() != null) { try { - int color = Integer.parseInt(systemPalette.getPackageName().toLowerCase(), 16); + String colorString = systemPalette.getPackageName().toLowerCase(); + if (!colorString.startsWith("#")) { + colorString = "#" + colorString; + } + int color = Color.parseColor(colorString); mNeutralOverlay = getOverlay(color, NEUTRAL); mNeedsOverlayCreation = true; categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE); - } catch (NumberFormatException e) { - Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName()); + } catch (Exception e) { + // Color.parseColor doesn't catch any exceptions from the calls it makes + Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName(), e); } } else if (!mIsMonetEnabled && systemPalette != null) { try { // It's possible that we flipped the flag off and still have a @ColorInt in the // setting. We need to sanitize the input, otherwise the overlay transaction will // fail. - Integer.parseInt(systemPalette.getPackageName().toLowerCase(), 16); categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE); } catch (NumberFormatException e) { // This is a package name. All good, let's continue @@ -473,12 +477,17 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { OverlayIdentifier accentPalette = categoryToPackage.get(OVERLAY_CATEGORY_ACCENT_COLOR); if (mIsMonetEnabled && accentPalette != null && accentPalette.getPackageName() != null) { try { - int color = Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16); + String colorString = accentPalette.getPackageName().toLowerCase(); + if (!colorString.startsWith("#")) { + colorString = "#" + colorString; + } + int color = Color.parseColor(colorString); mSecondaryOverlay = getOverlay(color, ACCENT); mNeedsOverlayCreation = true; categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR); - } catch (NumberFormatException e) { - Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName()); + } catch (Exception e) { + // Color.parseColor doesn't catch any exceptions from the calls it makes + Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName(), e); } } else if (!mIsMonetEnabled && accentPalette != null) { try { diff --git a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java index 361604c461b4..a40cf4f37cc3 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java @@ -101,7 +101,12 @@ abstract public class SafetyWarningDialog extends SystemUIDialog @Override public void onDismiss(DialogInterface unused) { - mContext.unregisterReceiver(mReceiver); + try { + mContext.unregisterReceiver(mReceiver); + } catch (IllegalArgumentException e) { + // Don't crash if the receiver has already been unregistered. + e.printStackTrace(); + } cleanUp(); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 5de7846a820e..3320852ca1f4 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -1743,7 +1743,7 @@ public class VolumeDialogImpl implements VolumeDialog, mContext, android.R.attr.colorBackgroundFloating); final ColorStateList inverseTextTint = Utils.getColorAttr( - mContext, com.android.internal.R.attr.textColorPrimaryInverse); + mContext, com.android.internal.R.attr.textColorOnAccent); row.sliderProgressSolid.setTintList(colorTint); if (row.sliderBgIcon != null) { |