diff options
Diffstat (limited to 'packages/SystemUI/src')
61 files changed, 1150 insertions, 997 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java index c89cda98c8a5..92f89d6b90fd 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java @@ -134,11 +134,15 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie }; @Override + protected void onInit() { + mIsDozing = mStatusBarStateController.isDozing(); + } + + @Override protected void onViewAttached() { updateLocale(); mBroadcastDispatcher.registerReceiver(mLocaleBroadcastReceiver, new IntentFilter(Intent.ACTION_LOCALE_CHANGED)); - mIsDozing = mStatusBarStateController.isDozing(); mDozeAmount = mStatusBarStateController.getDozeAmount(); mBatteryController.addCallback(mBatteryCallback); mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt index ff20805c5ea4..0785cc3c04d2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt @@ -23,5 +23,6 @@ data class KeyguardFaceListenModel( val isLockIconPressed: Boolean, val isScanningAllowedByStrongAuth: Boolean, val isPrimaryUser: Boolean, - val isSecureCameraLaunched: Boolean + val isSecureCameraLaunched: Boolean, + val isFaceAuthenticated: Boolean ) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java index 933a919efaad..92f8454fc93e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java @@ -27,6 +27,7 @@ import android.text.method.TextKeyListener; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup.MarginLayoutParams; +import android.view.WindowInsets; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; @@ -227,12 +228,16 @@ public class KeyguardPasswordViewController super.onPause(); }); } - mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0); + if (mPasswordEntry.isAttachedToWindow()) { + mPasswordEntry.getWindowInsetsController().hide(WindowInsets.Type.ime()); + } } @Override public void onStartingToHide() { - mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0); + if (mPasswordEntry.isAttachedToWindow()) { + mPasswordEntry.getWindowInsetsController().hide(WindowInsets.Type.ime()); + } } private void updateSwitchImeButton() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 5c9afa5cf4c9..33af16464965 100755 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -124,10 +124,13 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.TimeZone; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -470,16 +473,39 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab List<SubscriptionInfo> subscriptionInfos = getSubscriptionInfo(true /* forceReload */); // Hack level over 9000: Because the subscription id is not yet valid when we see the - // first update in handleSimStateChange, we need to force refresh all all SIM states + // first update in handleSimStateChange, we need to force refresh all SIM states // so the subscription id for them is consistent. ArrayList<SubscriptionInfo> changedSubscriptions = new ArrayList<>(); + Set<Integer> activeSubIds = new HashSet<>(); for (int i = 0; i < subscriptionInfos.size(); i++) { SubscriptionInfo info = subscriptionInfos.get(i); + activeSubIds.add(info.getSubscriptionId()); boolean changed = refreshSimState(info.getSubscriptionId(), info.getSimSlotIndex()); if (changed) { changedSubscriptions.add(info); } } + + // It is possible for active subscriptions to become invalid (-1), and these will not be + // present in the subscriptionInfo list + Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry<Integer, SimData> simData = iter.next(); + if (!activeSubIds.contains(simData.getKey())) { + Log.i(TAG, "Previously active sub id " + simData.getKey() + " is now invalid, " + + "will remove"); + iter.remove(); + + SimData data = simData.getValue(); + for (int j = 0; j < mCallbacks.size(); j++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get(); + if (cb != null) { + cb.onSimStateChanged(data.subId, data.slotId, data.simState); + } + } + } + } + for (int i = 0; i < changedSubscriptions.size(); i++) { SimData data = mSimDatas.get(changedSubscriptions.get(i).getSimSlotIndex()); for (int j = 0; j < mCallbacks.size(); j++) { @@ -1059,9 +1085,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab || isSimPinSecure()); } + private boolean getIsFaceAuthenticated() { + boolean faceAuthenticated = false; + BiometricAuthenticated bioFaceAuthenticated = mUserFaceAuthenticated.get(getCurrentUser()); + if (bioFaceAuthenticated != null) { + faceAuthenticated = bioFaceAuthenticated.mAuthenticated; + } + return faceAuthenticated; + } + private void requireStrongAuthIfAllLockedOut() { final boolean faceLock = - mFaceLockedOutPermanent || !shouldListenForFace(); + (mFaceLockedOutPermanent || !shouldListenForFace()) && !getIsFaceAuthenticated(); final boolean fpLock = mFingerprintLockedOutPermanent || !shouldListenForFingerprint(isUdfpsEnrolled()); @@ -1429,32 +1464,42 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final FaceManager.AuthenticationCallback mFaceAuthenticationCallback = new FaceManager.AuthenticationCallback() { - @Override - public void onAuthenticationFailed() { - handleFaceAuthFailed(); - } + @Override + public void onAuthenticationFailed() { + handleFaceAuthFailed(); + if (mKeyguardBypassController != null) { + mKeyguardBypassController.setUserHasDeviceEntryIntent(false); + } + } - @Override - public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) { - Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded"); - handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric()); - Trace.endSection(); - } + @Override + public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) { + Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded"); + handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric()); + Trace.endSection(); - @Override - public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { - handleFaceHelp(helpMsgId, helpString.toString()); - } + if (mKeyguardBypassController != null) { + mKeyguardBypassController.setUserHasDeviceEntryIntent(false); + } + } - @Override - public void onAuthenticationError(int errMsgId, CharSequence errString) { - handleFaceError(errMsgId, errString.toString()); - } + @Override + public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { + handleFaceHelp(helpMsgId, helpString.toString()); + } - @Override - public void onAuthenticationAcquired(int acquireInfo) { - handleFaceAcquired(acquireInfo); - } + @Override + public void onAuthenticationError(int errMsgId, CharSequence errString) { + handleFaceError(errMsgId, errString.toString()); + if (mKeyguardBypassController != null) { + mKeyguardBypassController.setUserHasDeviceEntryIntent(false); + } + } + + @Override + public void onAuthenticationAcquired(int acquireInfo) { + handleFaceAcquired(acquireInfo); + } }; private CancellationSignal mFingerprintCancelSignal; @@ -2232,6 +2277,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab strongAuthAllowsScanning = false; } + // If the face has recently been authenticated do not attempt to authenticate again. + boolean faceAuthenticated = getIsFaceAuthenticated(); + // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. final boolean shouldListen = @@ -2240,7 +2288,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab && !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer && !mKeyguardGoingAway && mBiometricEnabledForUser.get(user) && !mLockIconPressed && strongAuthAllowsScanning && mIsPrimaryUser - && (!mSecureCameraLaunched || mOccludingAppRequestingFace); + && (!mSecureCameraLaunched || mOccludingAppRequestingFace) + && !faceAuthenticated; // Aggregate relevant fields for debug logging. if (DEBUG_FACE || DEBUG_SPEW) { @@ -2261,7 +2310,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLockIconPressed, strongAuthAllowsScanning, mIsPrimaryUser, - mSecureCameraLaunched); + mSecureCameraLaunched, + faceAuthenticated); maybeLogFaceListenerModelData(model); } @@ -3386,6 +3436,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab pw.println(" trustManaged=" + getUserTrustIsManaged(userId)); pw.println(" udfpsEnrolled=" + isUdfpsEnrolled()); pw.println(" mFingerprintLockedOut=" + mFingerprintLockedOut); + pw.println(" mFingerprintLockedOutPermanent=" + mFingerprintLockedOutPermanent); pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId)); if (isUdfpsEnrolled()) { pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true)); @@ -3407,8 +3458,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab + getStrongAuthTracker().hasUserAuthenticatedSinceBoot()); pw.println(" disabled(DPM)=" + isFaceDisabled(userId)); pw.println(" possible=" + isUnlockWithFacePossible(userId)); + pw.println(" listening: actual=" + mFaceRunningState + + " expected=(" + (shouldListenForFace() ? 1 : 0)); pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags)); pw.println(" trustManaged=" + getUserTrustIsManaged(userId)); + pw.println(" mFaceLockedOutPermanent=" + mFaceLockedOutPermanent); pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId)); pw.println(" mSecureCameraLaunched=" + mSecureCameraLaunched); } diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index afea27222fbb..9c8582fa334d 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -28,6 +28,9 @@ import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.hardware.biometrics.BiometricSourceType; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.media.AudioAttributes; +import android.os.Process; +import android.os.Vibrator; import android.util.DisplayMetrics; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; @@ -44,6 +47,7 @@ import com.android.settingslib.Utils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.biometrics.UdfpsController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.FalsingManager; @@ -68,6 +72,16 @@ import javax.inject.Inject; */ @StatusBarComponent.StatusBarScope public class LockIconViewController extends ViewController<LockIconView> implements Dumpable { + private static final float sDefaultDensity = + (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT; + private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36); + private static final float sDistAboveKgBottomAreaPx = sDefaultDensity * 12; + private static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = + new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) + .build(); + @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @NonNull private final KeyguardViewController mKeyguardViewController; @NonNull private final StatusBarStateController mStatusBarStateController; @@ -82,8 +96,10 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @NonNull private final AnimatedVectorDrawable mFpToUnlockIcon; @NonNull private final AnimatedVectorDrawable mLockToUnlockIcon; @NonNull private final Drawable mLockIcon; + @NonNull private final Drawable mUnlockIcon; @NonNull private final CharSequence mUnlockedLabel; @NonNull private final CharSequence mLockedLabel; + @Nullable private final Vibrator mVibrator; private boolean mIsDozing; private boolean mIsBouncerShowing; @@ -98,14 +114,13 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private boolean mHasUdfps; private float mHeightPixels; private float mWidthPixels; - private float mDensity; - private int mAmbientIndicationHeight; // in pixels - private int mKgIndicationHeight; // in pixels + private int mBottomPadding; // in pixels private boolean mShowUnlockIcon; private boolean mShowLockIcon; private boolean mDownDetected; + private boolean mDetectedLongPress; private final Rect mSensorTouchLocation = new Rect(); @Inject @@ -120,7 +135,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @NonNull DumpManager dumpManager, @NonNull AccessibilityManager accessibilityManager, @NonNull ConfigurationController configurationController, - @NonNull @Main DelayableExecutor executor + @NonNull @Main DelayableExecutor executor, + @Nullable Vibrator vibrator ) { super(view); mStatusBarStateController = statusBarStateController; @@ -132,8 +148,13 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mAccessibilityManager = accessibilityManager; mConfigurationController = configurationController; mExecutor = executor; + mVibrator = vibrator; final Context context = view.getContext(); + mUnlockIcon = mView.getContext().getResources().getDrawable( + R.anim.lock_to_unlock, + mView.getContext().getTheme()); + ((AnimatedVectorDrawable) mUnlockIcon).start(); mLockIcon = mView.getContext().getResources().getDrawable( R.anim.lock_to_unlock, mView.getContext().getTheme()); @@ -213,8 +234,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme return; } - boolean wasShowingFpIcon = mHasUdfps && !mShowUnlockIcon && !mShowLockIcon; + boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon; boolean wasShowingLockIcon = mShowLockIcon; + boolean wasShowingUnlockIcon = mShowUnlockIcon; mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen() && (!mUdfpsEnrolled || !mRunningFPS); mShowUnlockIcon = mCanDismissLockScreen && isLockScreen(); @@ -225,14 +247,18 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mView.setVisibility(View.VISIBLE); mView.setContentDescription(mLockedLabel); } else if (mShowUnlockIcon) { - if (wasShowingFpIcon) { - mView.setImageDrawable(mFpToUnlockIcon); - mFpToUnlockIcon.forceAnimationOnUI(); - mFpToUnlockIcon.start(); - } else if (wasShowingLockIcon) { - mView.setImageDrawable(mLockToUnlockIcon); - mLockToUnlockIcon.forceAnimationOnUI(); - mLockToUnlockIcon.start(); + if (!wasShowingUnlockIcon) { + if (wasShowingFpIcon) { + mView.setImageDrawable(mFpToUnlockIcon); + mFpToUnlockIcon.forceAnimationOnUI(); + mFpToUnlockIcon.start(); + } else if (wasShowingLockIcon) { + mView.setImageDrawable(mLockToUnlockIcon); + mLockToUnlockIcon.forceAnimationOnUI(); + mLockToUnlockIcon.start(); + } else { + mView.setImageDrawable(mUnlockIcon); + } } mView.setVisibility(View.VISIBLE); mView.setContentDescription(mUnlockedLabel); @@ -286,17 +312,15 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mFpToUnlockIcon.setTint(color); mLockToUnlockIcon.setTint(color); mLockIcon.setTint(color); + mUnlockIcon.setTint(color); } private void updateConfiguration() { final DisplayMetrics metrics = mView.getContext().getResources().getDisplayMetrics(); mWidthPixels = metrics.widthPixels; mHeightPixels = metrics.heightPixels; - mDensity = metrics.density; - mKgIndicationHeight = mView.getContext().getResources().getDimensionPixelSize( - R.dimen.keyguard_indication_margin_bottom) - + mView.getContext().getResources().getDimensionPixelSize( - R.dimen.keyguard_indication_bottom_padding); + mBottomPadding = mView.getContext().getResources().getDimensionPixelSize( + R.dimen.lock_icon_margin_bottom); updateLockIconLocation(); } @@ -306,26 +330,15 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mView.setCenterLocation(new PointF(props.sensorLocationX, props.sensorLocationY), props.sensorRadius); } 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 - kgBottomAreaHeight - distAboveKgBottomArea - - radius / 2), (int) radius); + mHeightPixels - mBottomPadding - sDistAboveKgBottomAreaPx + - sLockIconRadiusPx), sLockIconRadiusPx); } 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); @@ -413,7 +426,16 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @Override public void onKeyguardShowingChanged() { + // Reset values in case biometrics were removed (ie: pin/pattern/password => swipe). + // If biometrics were removed, local vars mCanDismissLockScreen and + // mUserUnlockedWithBiometric may not be updated. + mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen(); updateKeyguardShowing(); + if (mIsKeyguardShowing) { + mUserUnlockedWithBiometric = + mKeyguardUpdateMonitor.getUserUnlockedWithBiometric( + KeyguardUpdateMonitor.getCurrentUser()); + } mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled(); updateVisibility(); } @@ -451,6 +473,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private final GestureDetector mGestureDetector = new GestureDetector(new SimpleOnGestureListener() { public boolean onDown(MotionEvent e) { + mDetectedLongPress = false; if (!isClickable()) { mDownDetected = false; return false; @@ -459,6 +482,14 @@ public class LockIconViewController extends ViewController<LockIconView> impleme // intercept all following touches until we see MotionEvent.ACTION_CANCEL UP or // MotionEvent.ACTION_UP (see #onTouchEvent) mDownDetected = true; + if (mVibrator != null) { + mVibrator.vibrate( + Process.myUid(), + getContext().getOpPackageName(), + UdfpsController.EFFECT_CLICK, + "lockIcon-onDown", + VIBRATION_SONIFICATION_ATTRIBUTES); + } return true; } @@ -467,6 +498,15 @@ public class LockIconViewController extends ViewController<LockIconView> impleme return; } + if (mVibrator != null) { + mVibrator.vibrate( + Process.myUid(), + getContext().getOpPackageName(), + UdfpsController.EFFECT_CLICK, + "lockIcon-onLongPress", + VIBRATION_SONIFICATION_ATTRIBUTES); + } + mDetectedLongPress = true; onAffordanceClick(); } @@ -509,7 +549,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme // we continue to intercept all following touches until we see MotionEvent.ACTION_CANCEL UP // or MotionEvent.ACTION_UP. this is to avoid passing the touch to NPV // after the lock icon disappears on device entry - if (mDownDetected) { + if (mDownDetected && mDetectedLongPress) { if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP) { mDownDetected = false; diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java index 099e6f4b5341..57407f1f34c0 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java @@ -88,13 +88,10 @@ public class NumPadButton extends AlphaOptimizedImageButton { * Reload colors from resources. **/ public void reloadColors() { - if (mAnimator != null) { - mAnimator.reloadColors(getContext()); - } else { - // Needed for old style pin - int textColor = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary) - .getDefaultColor(); - ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(textColor)); - } + if (mAnimator != null) mAnimator.reloadColors(getContext()); + + int textColor = Utils.getColorAttrDefaultColor(getContext(), + android.R.attr.colorBackground); + ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(textColor)); } } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 104d711f46fb..76f30a80114a 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -100,6 +100,7 @@ import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.statusbar.policy.AccessibilityController; @@ -359,9 +360,10 @@ public class Dependency { @Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager; @Inject Lazy<SystemStatusAnimationScheduler> mSystemStatusAnimationSchedulerLazy; @Inject Lazy<PrivacyDotViewController> mPrivacyDotViewControllerLazy; - @Inject Lazy<EdgeBackGestureHandler> mEdgeBackGestureHandler; + @Inject Lazy<EdgeBackGestureHandler.Factory> mEdgeBackGestureHandlerFactoryLazy; @Inject Lazy<UiEventLogger> mUiEventLogger; @Inject Lazy<FeatureFlags> mFeatureFlagsLazy; + @Inject Lazy<StatusBarContentInsetsProvider> mContentInsetsProviderLazy; @Inject public Dependency() { @@ -574,9 +576,11 @@ public class Dependency { mProviders.put(SystemStatusAnimationScheduler.class, mSystemStatusAnimationSchedulerLazy::get); mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get); - mProviders.put(EdgeBackGestureHandler.class, mEdgeBackGestureHandler::get); + mProviders.put(EdgeBackGestureHandler.Factory.class, + mEdgeBackGestureHandlerFactoryLazy::get); mProviders.put(UiEventLogger.class, mUiEventLogger::get); mProviders.put(FeatureFlags.class, mFeatureFlagsLazy::get); + mProviders.put(StatusBarContentInsetsProvider.class, mContentInsetsProviderLazy::get); Dependency.setInstance(this); } diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index 782cd29b0179..6a60afa8eadf 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -64,10 +64,6 @@ public final class Prefs { Key.QS_NIGHTDISPLAY_ADDED, Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, Key.SEEN_MULTI_USER, - Key.HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING, - Key.HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING, - Key.OVERVIEW_OPENED_COUNT, - Key.OVERVIEW_OPENED_FROM_HOME_COUNT, Key.SEEN_RINGER_GUIDANCE_COUNT, Key.QS_HAS_TURNED_OFF_MOBILE_DATA, Key.TOUCHED_RINGER_TOGGLE, @@ -109,14 +105,6 @@ public final class Prefs { */ String QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT = "QsLongPressTooltipShownCount"; String SEEN_MULTI_USER = "HasSeenMultiUser"; - String OVERVIEW_OPENED_COUNT = "OverviewOpenedCount"; - String OVERVIEW_OPENED_FROM_HOME_COUNT = "OverviewOpenedFromHomeCount"; - String HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING = "HasSeenRecentsSwipeUpOnboarding"; - String HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING = "HasSeenRecentsQuickScrubOnboarding"; - String DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT = - "DismissedRecentsSwipeUpOnboardingCount"; - String HAS_DISMISSED_RECENTS_QUICK_SCRUB_ONBOARDING_ONCE = - "HasDismissedRecentsQuickScrubOnboardingOnce"; String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount"; String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed"; String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData"; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 71e2bb657de4..0ce1846e7745 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -37,6 +37,7 @@ import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; +import android.hardware.display.DisplayManager; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintManager; @@ -57,6 +58,7 @@ import com.android.internal.os.SomeArgs; import com.android.systemui.SystemUI; import com.android.systemui.assist.ui.DisplayUtils; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.statusbar.CommandQueue; @@ -423,7 +425,9 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @Nullable FingerprintManager fingerprintManager, @Nullable FaceManager faceManager, Provider<UdfpsController> udfpsControllerFactory, - Provider<SidefpsController> sidefpsControllerFactory) { + Provider<SidefpsController> sidefpsControllerFactory, + @NonNull DisplayManager displayManager, + @Main Handler handler) { super(context); mCommandQueue = commandQueue; mActivityTaskManager = activityTaskManager; @@ -432,10 +436,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mUdfpsControllerFactory = udfpsControllerFactory; mSidefpsControllerFactory = sidefpsControllerFactory; mWindowManager = windowManager; - mOrientationListener = new BiometricOrientationEventListener(context, () -> { - onOrientationChanged(); - return Unit.INSTANCE; - }); + mOrientationListener = new BiometricOrientationEventListener(context, + () -> { + onOrientationChanged(); + return Unit.INSTANCE; + }, + displayManager, + handler); mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt index 08ea857eb208..98a03a1c444b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt @@ -17,7 +17,10 @@ package com.android.systemui.biometrics import android.content.Context +import android.hardware.display.DisplayManager +import android.os.Handler import android.view.OrientationEventListener +import android.view.Surface /** * An [OrientationEventListener] that invokes the [onOrientationChanged] callback whenever @@ -26,20 +29,16 @@ import android.view.OrientationEventListener */ class BiometricOrientationEventListener( private val context: Context, - private val onOrientationChanged: () -> Unit -) : OrientationEventListener(context) { + private val onOrientationChanged: () -> Unit, + private val displayManager: DisplayManager, + private val handler: Handler +) : DisplayManager.DisplayListener { - /** 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 - } + private var lastRotation = context.display?.rotation ?: Surface.ROTATION_0 + override fun onDisplayAdded(displayId: Int) {} + override fun onDisplayRemoved(displayId: Int) {} + override fun onDisplayChanged(displayId: Int) { val rotation = context.display?.rotation ?: return if (lastRotation != rotation) { lastRotation = rotation @@ -48,13 +47,11 @@ class BiometricOrientationEventListener( } } - override fun enable() { - enabled = true - super.enable() + fun enable() { + displayManager.registerDisplayListener(this, handler) } - override fun disable() { - enabled = false - super.disable() + fun disable() { + displayManager.unregisterDisplayListener(this) } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java index a51c2b802b91..8f6e2498a00b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java @@ -23,9 +23,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.graphics.PixelFormat; +import android.hardware.display.DisplayManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.ISidefpsController; +import android.os.Handler; import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; @@ -93,16 +95,22 @@ public class SidefpsController { @NonNull LayoutInflater inflater, @Nullable FingerprintManager fingerprintManager, @NonNull WindowManager windowManager, - @Main DelayableExecutor fgExecutor) { + @Main DelayableExecutor fgExecutor, + @NonNull DisplayManager displayManager, + @Main Handler handler) { mContext = context; mInflater = inflater; mFingerprintManager = checkNotNull(fingerprintManager); mWindowManager = windowManager; mFgExecutor = fgExecutor; - mOrientationListener = new BiometricOrientationEventListener(context, () -> { - onOrientationChanged(); - return Unit.INSTANCE; - }); + mOrientationListener = new BiometricOrientationEventListener( + context, + () -> { + onOrientationChanged(); + return Unit.INSTANCE; + }, + displayManager, + handler); mSensorProps = findFirstSidefps(); checkArgument(mSensorProps != null); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index ab3e042e9da7..950bd8319755 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -32,6 +32,7 @@ import android.content.IntentFilter; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.RectF; +import android.hardware.display.DisplayManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IUdfpsOverlayController; @@ -40,6 +41,7 @@ import android.media.AudioAttributes; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -67,8 +69,10 @@ import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; @@ -106,6 +110,7 @@ public class UdfpsController implements DozeReceiver { private final DelayableExecutor mFgExecutor; @NonNull private final StatusBar mStatusBar; @NonNull private final StatusBarStateController mStatusBarStateController; + @NonNull private final KeyguardStateController mKeyguardStateController; @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; @NonNull private final DumpManager mDumpManager; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -117,6 +122,7 @@ public class UdfpsController implements DozeReceiver { @NonNull private final AccessibilityManager mAccessibilityManager; @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; @Nullable private final UdfpsHbmProvider mHbmProvider; + @NonNull private final KeyguardBypassController mKeyguardBypassController; @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. @@ -148,6 +154,7 @@ public class UdfpsController implements DozeReceiver { private boolean mScreenOn; private Runnable mAodInterruptRunnable; private boolean mOnFingerDown; + private boolean mAttemptedToDismissKeyguard; @VisibleForTesting public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = @@ -379,8 +386,12 @@ public class UdfpsController implements DozeReceiver { // ACTION_DOWN, in that case we should just reuse the old instance. mVelocityTracker.clear(); } - if (isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView)) { + + boolean withinSensorArea = + isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView); + if (withinSensorArea) { Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0); + Log.v(TAG, "onTouch | action down"); // The pointer that causes ACTION_DOWN is always at index 0. // We need to persist its ID to track it during ACTION_MOVE that could include // data for many other pointers because of multi-touch support. @@ -388,6 +399,14 @@ public class UdfpsController implements DozeReceiver { mVelocityTracker.addMovement(event); handled = true; } + if ((withinSensorArea || fromUdfpsView) && shouldTryToDismissKeyguard()) { + Log.v(TAG, "onTouch | dismiss keyguard ACTION_DOWN"); + if (!mOnFingerDown) { + playStartHaptic(); + } + mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */); + mAttemptedToDismissKeyguard = true; + } Trace.endSection(); break; @@ -398,8 +417,20 @@ public class UdfpsController implements DozeReceiver { ? event.getPointerId(0) : event.findPointerIndex(mActivePointerId); if (idx == event.getActionIndex()) { - if (isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx), - fromUdfpsView)) { + boolean actionMoveWithinSensorArea = + isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx), + fromUdfpsView); + if ((fromUdfpsView || actionMoveWithinSensorArea) + && shouldTryToDismissKeyguard()) { + Log.v(TAG, "onTouch | dismiss keyguard ACTION_MOVE"); + if (!mOnFingerDown) { + playStartHaptic(); + } + mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */); + mAttemptedToDismissKeyguard = true; + break; + } + if (actionMoveWithinSensorArea) { if (mVelocityTracker == null) { // touches could be injected, so the velocity tracker may not have // been initialized (via ACTION_DOWN). @@ -448,6 +479,7 @@ public class UdfpsController implements DozeReceiver { mVelocityTracker = null; } Log.v(TAG, "onTouch | finger up"); + mAttemptedToDismissKeyguard = false; onFingerUp(); mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION); Trace.endSection(); @@ -459,6 +491,13 @@ public class UdfpsController implements DozeReceiver { return handled; } + private boolean shouldTryToDismissKeyguard() { + return mView.getAnimationViewController() != null + && mView.getAnimationViewController() instanceof UdfpsKeyguardViewController + && mKeyguardStateController.canDismissLockScreen() + && !mAttemptedToDismissKeyguard; + } + @Inject public UdfpsController(@NonNull Context context, @NonNull Execution execution, @@ -479,7 +518,11 @@ public class UdfpsController implements DozeReceiver { @NonNull ScreenLifecycle screenLifecycle, @Nullable Vibrator vibrator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, - @NonNull Optional<UdfpsHbmProvider> hbmProvider) { + @NonNull Optional<UdfpsHbmProvider> hbmProvider, + @NonNull KeyguardStateController keyguardStateController, + @NonNull KeyguardBypassController keyguardBypassController, + @NonNull DisplayManager displayManager, + @Main Handler mainHandler) { mContext = context; mExecution = execution; // TODO (b/185124905): inject main handler and vibrator once done prototyping @@ -493,6 +536,7 @@ public class UdfpsController implements DozeReceiver { mFgExecutor = fgExecutor; mStatusBar = statusBar; mStatusBarStateController = statusBarStateController; + mKeyguardStateController = keyguardStateController; mKeyguardViewManager = statusBarKeyguardViewManager; mDumpManager = dumpManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; @@ -504,10 +548,15 @@ 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; - }); + mOrientationListener = new BiometricOrientationEventListener( + context, + () -> { + onOrientationChanged(); + return Unit.INSTANCE; + }, + displayManager, + mainHandler); + mKeyguardBypassController = keyguardBypassController; mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists @@ -539,7 +588,12 @@ public class UdfpsController implements DozeReceiver { @VisibleForTesting public void playStartHaptic() { if (mVibrator != null) { - mVibrator.vibrate(EFFECT_CLICK, VIBRATION_SONIFICATION_ATTRIBUTES); + mVibrator.vibrate( + Process.myUid(), + mContext.getOpPackageName(), + EFFECT_CLICK, + "udfps-onStart", + VIBRATION_SONIFICATION_ATTRIBUTES); } } @@ -615,7 +669,8 @@ public class UdfpsController implements DozeReceiver { // Transform dimensions if the device is in landscape mode switch (mContext.getDisplay().getRotation()) { case Surface.ROTATION_90: - if (animation instanceof UdfpsKeyguardViewController) { + if (animation instanceof UdfpsKeyguardViewController + && mKeyguardUpdateMonitor.isGoingToSleep()) { break; } mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius @@ -625,7 +680,8 @@ public class UdfpsController implements DozeReceiver { break; case Surface.ROTATION_270: - if (animation instanceof UdfpsKeyguardViewController) { + if (animation instanceof UdfpsKeyguardViewController + && mKeyguardUpdateMonitor.isGoingToSleep()) { break; } mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius @@ -667,6 +723,7 @@ public class UdfpsController implements DozeReceiver { mView.setSensorProperties(mSensorProps); mView.setHbmProvider(mHbmProvider); UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason); + mAttemptedToDismissKeyguard = false; animation.init(); mView.setAnimationViewController(animation); mOrientationListener.enable(); @@ -826,12 +883,17 @@ public class UdfpsController implements DozeReceiver { private void onFingerDown(int x, int y, float minor, float major) { mExecution.assertIsMainThread(); + mKeyguardBypassController.setUserHasDeviceEntryIntent(true); if (mView == null) { Log.w(TAG, "Null view in onFingerDown"); return; } if (!mOnFingerDown) { playStartHaptic(); + + if (!mKeyguardUpdateMonitor.isFaceDetectionRunning()) { + mKeyguardUpdateMonitor.requestFaceAuth(/* userInitiatedRequest */ false); + } } mOnFingerDown = true; mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java index 412b77698159..6a918a6c8d39 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java @@ -21,11 +21,8 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.PointF; import android.hardware.fingerprint.IUdfpsOverlayController; -import android.media.AudioAttributes; import android.os.Build; import android.os.UserHandle; -import android.os.VibrationEffect; -import android.os.Vibrator; import android.provider.Settings; import android.util.Log; import android.util.TypedValue; @@ -50,12 +47,6 @@ public class UdfpsEnrollHelper { // Enroll with two center touches before going to guided enrollment private static final int NUM_CENTER_TOUCHES = 2; - private static final AudioAttributes VIBRATION_SONFICATION_ATTRIBUTES = - new AudioAttributes.Builder() - .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) - .build(); - interface Listener { void onEnrollmentProgress(int remaining, int totalSteps); void onLastStepAcquired(); @@ -66,9 +57,6 @@ public class UdfpsEnrollHelper { private final int mEnrollReason; private final boolean mAccessibilityEnabled; @NonNull private final List<PointF> mGuidedEnrollmentPoints; - @NonNull private final Vibrator mVibrator; - @NonNull private final VibrationEffect mEffectClick = - VibrationEffect.get(VibrationEffect.EFFECT_CLICK); private int mTotalSteps = -1; private int mRemainingSteps = -1; @@ -82,7 +70,6 @@ public class UdfpsEnrollHelper { public UdfpsEnrollHelper(@NonNull Context context, int reason) { mContext = context; mEnrollReason = reason; - mVibrator = context.getSystemService(Vibrator.class); final AccessibilityManager am = context.getSystemService(AccessibilityManager.class); mAccessibilityEnabled = am.isEnabled(); @@ -141,7 +128,6 @@ public class UdfpsEnrollHelper { if (remaining != mRemainingSteps) { mLocationsEnrolled++; - vibrateSuccess(); } mRemainingSteps = remaining; @@ -202,11 +188,6 @@ public class UdfpsEnrollHelper { if (mRemainingSteps <= 2 && mRemainingSteps >= 0) { mListener.onLastStepAcquired(); - vibrateSuccess(); } } - - private void vibrateSuccess() { - mVibrator.vibrate(mEffectClick, VIBRATION_SONFICATION_ATTRIBUTES); - } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java index 131618442c22..eb02aa0d9cdf 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -162,7 +162,12 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { } void updateColor() { + mWallpaperTextColor = Utils.getColorAttrDefaultColor(mContext, + R.attr.wallpaperTextColorAccent); + mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorPrimary); mLockScreenFp.invalidate(); + mBgProtection.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg)); } private boolean showingUdfpsBouncer() { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 51124fb28ad1..6435bdd69872 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -377,6 +377,9 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @Override public boolean onTouch(MotionEvent event) { + if (mTransitionToFullShadeProgress != 0) { + return false; + } return mUdfpsController.onTouch(event); } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt index c241c083a0a3..4104e3184efd 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt @@ -64,16 +64,12 @@ class ControlsActivity @Inject constructor( } } - override fun onStart() { - super.onStart() + override fun onResume() { + super.onResume() parent = requireViewById<ViewGroup>(R.id.global_actions_controls) parent.alpha = 0f uiController.show(parent, { finish() }, this) - } - - override fun onResume() { - super.onResume() ControlsAnimations.enterAnimation(parent).start() } @@ -82,8 +78,8 @@ class ControlsActivity @Inject constructor( finish() } - override fun onStop() { - super.onStop() + override fun onPause() { + super.onPause() uiController.hide() } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index 5acb3038b91b..06e74821869e 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -669,11 +669,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene int userId = user.id; - // No lockdown option if it's not turned on in Settings - if (mSecureSettings.getIntForUser(Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, userId) == 0) { - return false; - } - // Lockdown is meaningless without a place to go. if (!mKeyguardStateController.isMethodSecure()) { return false; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 26f38ddd5919..cb11454e44ee 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -1047,10 +1047,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, // Returns true if the bar mode is changed. private boolean updateBarMode(int barMode) { if (mNavigationBarMode != barMode) { - if (mNavigationBarMode == MODE_TRANSPARENT - || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) { - mNavigationBarView.hideRecentsOnboarding(); - } mNavigationBarMode = barMode; checkNavBarModes(); if (mAutoHideController != null) { @@ -1620,6 +1616,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + // TODO(193941146): Currently unregistering a receiver through BroadcastDispatcher is + // async, but we've already cleared the fields. Just return early in this case. + if (mNavigationBarView == null) { + return; + } String action = intent.getAction(); if (Intent.ACTION_SCREEN_OFF.equals(action) || Intent.ACTION_SCREEN_ON.equals(action)) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 4816f1cf8d6a..61e803312b55 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -81,7 +81,6 @@ import com.android.systemui.navigationbar.gestural.FloatingRotationButton; import com.android.systemui.navigationbar.gestural.RegionSamplingHelper; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; -import com.android.systemui.recents.RecentsOnboarding; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; @@ -163,7 +162,6 @@ public class NavigationBarView extends FrameLayout implements private Configuration mTmpLastConfiguration; private NavigationBarInflaterView mNavigationInflaterView; - private RecentsOnboarding mRecentsOnboarding; private NotificationPanelViewController mPanelView; private RotationContextButton mRotationContextButton; private FloatingRotationButton mFloatingRotationButton; @@ -322,7 +320,6 @@ public class NavigationBarView extends FrameLayout implements updateRotationButton(); mOverviewProxyService = Dependency.get(OverviewProxyService.class); - mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService); mConfiguration = new Configuration(); mTmpLastConfiguration = new Configuration(); @@ -342,7 +339,8 @@ public class NavigationBarView extends FrameLayout implements mNavColorSampleMargin = getResources() .getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin); - mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.class); + mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class) + .create(mContext); mEdgeBackGestureHandler.setStateChangeCallback(this::updateStates); mRegionSamplingHelper = new RegionSamplingHelper(this, new RegionSamplingHelper.SamplingCallback() { @@ -696,7 +694,6 @@ public class NavigationBarView extends FrameLayout implements updateNavButtonIcons(); updateSlippery(); - setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); updateDisabledSystemUiStateFlags(); } @@ -872,7 +869,6 @@ public class NavigationBarView extends FrameLayout implements updateSlippery(); reloadNavIcons(); updateNavButtonIcons(); - setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled(!showSwipeUpUI); getHomeButton().setAccessibilityDelegate( showSwipeUpUI ? mQuickStepAccessibilityDelegate : null); @@ -916,7 +912,6 @@ public class NavigationBarView extends FrameLayout implements mNavBarMode = mode; mBarTransitions.onNavigationModeChanged(mNavBarMode); mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode); - mRecentsOnboarding.onNavigationModeChanged(mNavBarMode); updateRotationButton(); if (isGesturalMode(mNavBarMode)) { @@ -932,10 +927,6 @@ public class NavigationBarView extends FrameLayout implements mContextualButtonGroup.setButtonVisibility(R.id.accessibility_button, visible); } - void hideRecentsOnboarding() { - mRecentsOnboarding.hide(true); - } - @Override public void onFinishInflate() { super.onFinishInflate(); @@ -980,7 +971,6 @@ public class NavigationBarView extends FrameLayout implements super.onLayout(changed, left, top, right, bottom); notifyActiveTouchRegions(); - mRecentsOnboarding.setNavBarHeight(getMeasuredHeight()); } /** @@ -1196,7 +1186,6 @@ public class NavigationBarView extends FrameLayout implements updateIcons(mTmpLastConfiguration); updateRecentsIcon(); mEdgeBackGestureHandler.onConfigurationChanged(mConfiguration); - mRecentsOnboarding.onConfigurationChanged(mConfiguration); if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) { // If car mode or density changes, we need to reset the icons. @@ -1260,7 +1249,6 @@ public class NavigationBarView extends FrameLayout implements requestApplyInsets(); reorient(); onNavigationModeChanged(mNavBarMode); - setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); if (mRotationButtonController != null) { mRotationButtonController.registerListeners(); } @@ -1276,7 +1264,6 @@ public class NavigationBarView extends FrameLayout implements protected void onDetachedFromWindow() { super.onDetachedFromWindow(); Dependency.get(NavigationModeController.class).removeListener(this); - setUpSwipeUpOnboarding(false); for (int i = 0; i < mButtonDispatchers.size(); ++i) { mButtonDispatchers.valueAt(i).onDestroy(); } @@ -1293,14 +1280,6 @@ public class NavigationBarView extends FrameLayout implements mOnComputeInternalInsetsListener); } - private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) { - if (connectedToOverviewProxy) { - mRecentsOnboarding.onConnectedToLauncher(); - } else { - mRecentsOnboarding.onDisconnectedFromLauncher(); - } - } - public void dump(PrintWriter pw) { final Rect r = new Rect(); final Point size = new Point(); @@ -1344,7 +1323,6 @@ public class NavigationBarView extends FrameLayout implements } mBarTransitions.dump(pw); mContextualButtonGroup.dump(pw); - mRecentsOnboarding.dump(pw); mRegionSamplingHelper.dump(pw); mEdgeBackGestureHandler.dump(pw); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index ff5d0b157c80..aaa3bf0f40ee 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -58,7 +58,6 @@ import com.android.internal.policy.GestureNavigationSettingsObserver; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationBarView; @@ -92,7 +91,6 @@ import javax.inject.Inject; /** * Utility class to handle edge swipes for back gesture */ -@SysUISingleton public class EdgeBackGestureHandler extends CurrentUserTracker implements PluginListener<NavigationEdgeBackPlugin>, ProtoTraceable<SystemUiTraceProto> { @@ -294,8 +292,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker } }; - @Inject - public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService, + + EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService, SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer, NavigationModeController navigationModeController, ViewConfiguration viewConfiguration, @@ -919,6 +917,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker pw.println(" mGestureLogInsideInsets=" + String.join("\n", mGestureLogInsideInsets)); pw.println(" mGestureLogOutsideInsets=" + String.join("\n", mGestureLogOutsideInsets)); pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin); + if (mEdgeBackPlugin != null) { + mEdgeBackPlugin.dump(pw); + } } private boolean isGestureBlockingActivityRunning() { @@ -941,6 +942,53 @@ public class EdgeBackGestureHandler extends CurrentUserTracker proto.edgeBackGestureHandler.allowGesture = mAllowGesture; } + /** + * Injectable instance to create a new EdgeBackGestureHandler. + * + * Necessary because we don't have good handling of per-display contexts at the moment. With + * this, you can pass in a specific context that knows what display it is in. + */ + public static class Factory { + private final OverviewProxyService mOverviewProxyService; + private final SysUiState mSysUiState; + private final PluginManager mPluginManager; + private final Executor mExecutor; + private final BroadcastDispatcher mBroadcastDispatcher; + private final ProtoTracer mProtoTracer; + private final NavigationModeController mNavigationModeController; + private final ViewConfiguration mViewConfiguration; + private final WindowManager mWindowManager; + private final IWindowManager mWindowManagerService; + private final FalsingManager mFalsingManager; + + @Inject + public Factory(OverviewProxyService overviewProxyService, + SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, + BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer, + NavigationModeController navigationModeController, + ViewConfiguration viewConfiguration, WindowManager windowManager, + IWindowManager windowManagerService, FalsingManager falsingManager) { + mOverviewProxyService = overviewProxyService; + mSysUiState = sysUiState; + mPluginManager = pluginManager; + mExecutor = executor; + mBroadcastDispatcher = broadcastDispatcher; + mProtoTracer = protoTracer; + mNavigationModeController = navigationModeController; + mViewConfiguration = viewConfiguration; + mWindowManager = windowManager; + mWindowManagerService = windowManagerService; + mFalsingManager = falsingManager; + } + + /** Construct a {@link EdgeBackGestureHandler}. */ + public EdgeBackGestureHandler create(Context context) { + return new EdgeBackGestureHandler(context, mOverviewProxyService, mSysUiState, + mPluginManager, mExecutor, mBroadcastDispatcher, mProtoTracer, + mNavigationModeController, mViewConfiguration, mWindowManager, + mWindowManagerService, mFalsingManager); + } + } private static class LogArray extends ArrayDeque<String> { private final int mLength; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java index 9c82989531aa..7fdb79eae2a9 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java @@ -30,6 +30,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.graphics.Rect; +import android.os.Handler; import android.os.SystemClock; import android.os.VibrationEffect; import android.util.Log; @@ -56,13 +57,18 @@ import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.NavigationEdgeBackPlugin; import com.android.systemui.statusbar.VibratorHelper; +import java.io.PrintWriter; + public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPlugin { private static final String TAG = "NavigationBarEdgePanel"; + private static final boolean ENABLE_FAILSAFE = true; + private static final long COLOR_ANIMATION_DURATION_MS = 120; private static final long DISAPPEAR_FADE_ANIMATION_DURATION_MS = 80; private static final long DISAPPEAR_ARROW_ANIMATION_DURATION_MS = 100; + private static final long FAILSAFE_DELAY_MS = 200; /** * The time required since the first vibration effect to automatically trigger a click @@ -215,6 +221,9 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl private long mVibrationTime; private int mScreenSize; + private final Handler mHandler = new Handler(); + private final Runnable mFailsafeRunnable = this::onFailsafe; + private DynamicAnimation.OnAnimationEndListener mSetGoneEndListener = new DynamicAnimation.OnAnimationEndListener() { @Override @@ -364,6 +373,7 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl @Override public void onDestroy() { + cancelFailsafe(); mWindowManager.removeView(this); mRegionSamplingHelper.stop(); mRegionSamplingHelper = null; @@ -635,6 +645,8 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl animate().alpha(0f).setDuration(DISAPPEAR_FADE_ANIMATION_DURATION_MS) .withEndAction(() -> setVisibility(GONE)); mArrowDisappearAnimation.start(); + // Schedule failsafe in case alpha end callback is not called + scheduleFailsafe(); }; if (mTranslationAnimation.isRunning()) { mTranslationAnimation.addEndListener(new DynamicAnimation.OnAnimationEndListener() { @@ -648,6 +660,8 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl } } }); + // Schedule failsafe in case mTranslationAnimation end callback is not called + scheduleFailsafe(); } else { translationEnd.run(); } @@ -658,6 +672,8 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl if (mTranslationAnimation.isRunning()) { mTranslationAnimation.addEndListener(mSetGoneEndListener); + // Schedule failsafe in case mTranslationAnimation end callback is not called + scheduleFailsafe(); } else { setVisibility(GONE); } @@ -683,6 +699,7 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl mTotalTouchDelta = 0; mVibrationTime = 0; setDesiredVerticalTransition(0, false /* animated */); + cancelFailsafe(); } private void handleMoveEvent(MotionEvent event) { @@ -867,7 +884,37 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl invalidate(); } + private void scheduleFailsafe() { + if (!ENABLE_FAILSAFE) { + return; + } + cancelFailsafe(); + mHandler.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY_MS); + } + + private void cancelFailsafe() { + mHandler.removeCallbacks(mFailsafeRunnable); + } + + private void onFailsafe() { + setVisibility(GONE); + } + private float dp(float dp) { return mDensity * dp; } + + @Override + public void dump(PrintWriter pw) { + pw.println("NavigationBarEdgePanel:"); + pw.println(" mIsLeftPanel=" + mIsLeftPanel); + pw.println(" mTriggerBack=" + mTriggerBack); + pw.println(" mDragSlopPassed=" + mDragSlopPassed); + pw.println(" mCurrentAngle=" + mCurrentAngle); + pw.println(" mDesiredAngle=" + mDesiredAngle); + pw.println(" mCurrentTranslation=" + mCurrentTranslation); + pw.println(" mDesiredTranslation=" + mDesiredTranslation); + pw.println(" mTranslationAnimation running=" + mTranslationAnimation.isRunning()); + mRegionSamplingHelper.dump(pw); + } } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java index e4f5cde37f1d..d94cd28c3bc6 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java +++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java @@ -53,6 +53,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import javax.inject.Inject; @@ -88,6 +89,9 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl private static final int STATE_COLLAPSED = 3; private static final int STATE_DISAPPEARING = 4; + // Avoid multiple messages after rapid changes such as starting/stopping both camera and mic. + private static final int ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS = 500; + private static final int EXPANDED_DURATION_MS = 4000; public final int mAnimationDurationMs; @@ -113,6 +117,9 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl private final Handler mUiThreadHandler = new Handler(Looper.getMainLooper()); private final Runnable mCollapseRunnable = this::collapseChip; + private final Runnable mAccessibilityRunnable = this::makeAccessibilityAnnouncement; + private final List<PrivacyItem> mItemsBeforeLastAnnouncement = new LinkedList<>(); + @State private int mState = STATE_NOT_SHOWN; @@ -169,6 +176,8 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl } mPrivacyItems = updatedPrivacyItems; + + postAccessibilityAnnouncement(); updateChip(); } @@ -301,6 +310,7 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener( this); + postAccessibilityAnnouncement(); animateIconAppearance(); mChipDrawable.startInitialFadeIn(); } @@ -460,6 +470,83 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl } /** + * Schedules the accessibility announcement to be made after {@code + * ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS} (if possible). This is so that only one announcement is + * made instead of two separate ones if both the camera and the mic are started/stopped. + */ + private void postAccessibilityAnnouncement() { + mUiThreadHandler.removeCallbacks(mAccessibilityRunnable); + + if (mPrivacyItems.size() == 0) { + // Announce immediately since announcement cannot be made once the chip is gone. + makeAccessibilityAnnouncement(); + } else { + mUiThreadHandler.postDelayed(mAccessibilityRunnable, + ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS); + } + } + + private void makeAccessibilityAnnouncement() { + if (mIndicatorView == null) { + return; + } + + boolean cameraWasRecording = listContainsPrivacyType(mItemsBeforeLastAnnouncement, + PrivacyType.TYPE_CAMERA); + boolean cameraIsRecording = listContainsPrivacyType(mPrivacyItems, + PrivacyType.TYPE_CAMERA); + boolean micWasRecording = listContainsPrivacyType(mItemsBeforeLastAnnouncement, + PrivacyType.TYPE_MICROPHONE); + boolean micIsRecording = listContainsPrivacyType(mPrivacyItems, + PrivacyType.TYPE_MICROPHONE); + + int announcement = 0; + if (!cameraWasRecording && cameraIsRecording && !micWasRecording && micIsRecording) { + // Both started + announcement = R.string.mic_and_camera_recording_announcement; + } else if (cameraWasRecording && !cameraIsRecording && micWasRecording && !micIsRecording) { + // Both stopped + announcement = R.string.mic_camera_stopped_recording_announcement; + } else { + // Did the camera start or stop? + if (cameraWasRecording && !cameraIsRecording) { + announcement = R.string.camera_stopped_recording_announcement; + } else if (!cameraWasRecording && cameraIsRecording) { + announcement = R.string.camera_recording_announcement; + } + + // Announce camera changes now since we might need a second announcement about the mic. + if (announcement != 0) { + mIndicatorView.announceForAccessibility(mContext.getString(announcement)); + announcement = 0; + } + + // Did the mic start or stop? + if (micWasRecording && !micIsRecording) { + announcement = R.string.mic_stopped_recording_announcement; + } else if (!micWasRecording && micIsRecording) { + announcement = R.string.mic_recording_announcement; + } + } + + if (announcement != 0) { + mIndicatorView.announceForAccessibility(mContext.getString(announcement)); + } + + mItemsBeforeLastAnnouncement.clear(); + mItemsBeforeLastAnnouncement.addAll(mPrivacyItems); + } + + private boolean listContainsPrivacyType(List<PrivacyItem> list, PrivacyType privacyType) { + for (PrivacyItem item : list) { + if (item.getPrivacyType() == privacyType) { + return true; + } + } + return false; + } + + /** * Used in debug logs. */ private String stateToString(@State int state) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 03a2c843a15e..cd9db614e477 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -32,6 +32,8 @@ import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.Space; +import androidx.annotation.NonNull; + import com.android.settingslib.Utils; import com.android.systemui.BatteryMeterView; import com.android.systemui.R; @@ -41,6 +43,8 @@ import com.android.systemui.statusbar.phone.StatusBarWindowView; import com.android.systemui.statusbar.phone.StatusIconContainer; import com.android.systemui.statusbar.policy.Clock; +import java.util.List; + /** * View that contains the top-most bits of the QS panel (primarily the status bar with date, time, * battery, carrier info and privacy icons) and also contains the {@link QuickQSPanel}. @@ -86,18 +90,13 @@ public class QuickStatusBarHeader extends FrameLayout { private float mKeyguardExpansionFraction; private int mTextColorPrimary = Color.TRANSPARENT; private int mTopViewMeasureHeight; - private boolean mProviderModel; - private final String mMobileSlotName; - private final String mNoCallingSlotName; - private final String mCallStrengthSlotName; + @NonNull + private List<String> mRssiIgnoredSlots; + private boolean mIsSingleCarrier; public QuickStatusBarHeader(Context context, AttributeSet attrs) { super(context, attrs); - mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_mobile); - mNoCallingSlotName = context.getString(com.android.internal.R.string.status_bar_no_calling); - mCallStrengthSlotName = - context.getString(com.android.internal.R.string.status_bar_call_strength); } /** @@ -148,9 +147,9 @@ public class QuickStatusBarHeader extends FrameLayout { void onAttach(TintedIconManager iconManager, QSExpansionPathInterpolator qsExpansionPathInterpolator, - boolean providerModel) { - mProviderModel = providerModel; + List<String> rssiIgnoredSlots) { mTintedIconManager = iconManager; + mRssiIgnoredSlots = rssiIgnoredSlots; int fillColor = Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary); @@ -161,6 +160,11 @@ public class QuickStatusBarHeader extends FrameLayout { updateAnimators(); } + void setIsSingleCarrier(boolean isSingleCarrier) { + mIsSingleCarrier = isSingleCarrier; + updateAlphaAnimator(); + } + public QuickQSPanel getHeaderQsPanel() { return mHeaderQsPanel; } @@ -267,39 +271,26 @@ public class QuickStatusBarHeader extends FrameLayout { .setListener(new TouchAnimator.ListenerAdapter() { @Override public void onAnimationAtEnd() { - // TODO(b/185580157): Remove the mProviderModel if the mobile slot can be - // hidden in Provider model. - if (mProviderModel) { - mIconContainer.addIgnoredSlot(mNoCallingSlotName); - mIconContainer.addIgnoredSlot(mCallStrengthSlotName); - } else { - mIconContainer.addIgnoredSlot(mMobileSlotName); + super.onAnimationAtEnd(); + if (!mIsSingleCarrier) { + mIconContainer.addIgnoredSlots(mRssiIgnoredSlots); } } @Override public void onAnimationStarted() { - if (mProviderModel) { - mIconContainer.addIgnoredSlot(mNoCallingSlotName); - mIconContainer.addIgnoredSlot(mCallStrengthSlotName); - } else { - mIconContainer.addIgnoredSlot(mMobileSlotName); - } - setSeparatorVisibility(false); + if (!mIsSingleCarrier) { + mIconContainer.addIgnoredSlots(mRssiIgnoredSlots); + } } @Override public void onAnimationAtStart() { super.onAnimationAtStart(); - if (mProviderModel) { - mIconContainer.removeIgnoredSlot(mNoCallingSlotName); - mIconContainer.removeIgnoredSlot(mCallStrengthSlotName); - } else { - mIconContainer.removeIgnoredSlot(mMobileSlotName); - } - setSeparatorVisibility(mShowClockIconsSeparator); + // In QQS we never ignore RSSI. + mIconContainer.removeIgnoredSlots(mRssiIgnoredSlots); } }); mAlphaAnimator = builder.build(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index fcf1302b8fb4..b8b7f42455bb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -43,7 +43,6 @@ import com.android.systemui.statusbar.phone.StatusIconContainer; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.util.ViewController; -import java.util.ArrayList; import java.util.List; import javax.inject.Inject; @@ -76,6 +75,9 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader private boolean mMicCameraIndicatorsEnabled; private boolean mLocationIndicatorsEnabled; private boolean mPrivacyChipLogged; + private final String mCameraSlot; + private final String mMicSlot; + private final String mLocationSlot; private SysuiColorExtractor mColorExtractor; private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener; @@ -104,8 +106,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader } private void update() { - StatusIconContainer iconContainer = mView.requireViewById(R.id.statusIcons); - iconContainer.setIgnoredSlots(getIgnoredIconSlots()); + updatePrivacyIconSlots(); setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty()); } }; @@ -154,7 +155,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mClockView = mView.findViewById(R.id.clock); mIconContainer = mView.findViewById(R.id.statusIcons); - mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, mFeatureFlags); + mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, featureFlags); mDemoModeReceiver = new ClockDemoModeReceiver(mClockView); mColorExtractor = colorExtractor; mOnColorsChangedListener = (extractor, which) -> { @@ -162,6 +163,10 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mClockView.onColorsChanged(lightTheme); }; mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener); + + mCameraSlot = getResources().getString(com.android.internal.R.string.status_bar_camera); + mMicSlot = getResources().getString(com.android.internal.R.string.status_bar_microphone); + mLocationSlot = getResources().getString(com.android.internal.R.string.status_bar_location); } @Override @@ -172,14 +177,30 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable(); // Ignore privacy icons because they show in the space above QQS - mIconContainer.setIgnoredSlots(getIgnoredIconSlots()); + updatePrivacyIconSlots(); mIconContainer.setShouldRestrictIcons(false); mStatusBarIconController.addIconGroup(mIconManager); setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE); - mView.onAttach(mIconManager, mQSExpansionPathInterpolator, - mFeatureFlags.isCombinedStatusBarSignalIconsEnabled()); + mView.setIsSingleCarrier(mQSCarrierGroupController.isSingleCarrier()); + mQSCarrierGroupController + .setOnSingleCarrierChangedListener(mView::setIsSingleCarrier); + + List<String> rssiIgnoredSlots; + + if (mFeatureFlags.isCombinedStatusBarSignalIconsEnabled()) { + rssiIgnoredSlots = List.of( + getResources().getString(com.android.internal.R.string.status_bar_no_calling), + getResources().getString(com.android.internal.R.string.status_bar_call_strength) + ); + } else { + rssiIgnoredSlots = List.of( + getResources().getString(com.android.internal.R.string.status_bar_mobile) + ); + } + + mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots); mDemoModeController.addCallback(mDemoModeReceiver); } @@ -189,6 +210,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener); mPrivacyChip.setOnClickListener(null); mStatusBarIconController.removeIconGroup(mIconManager); + mQSCarrierGroupController.setOnSingleCarrierChangedListener(null); mDemoModeController.removeCallback(mDemoModeReceiver); setListening(false); } @@ -236,21 +258,25 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mView.setChipVisibility(chipVisible); } - private List<String> getIgnoredIconSlots() { - ArrayList<String> ignored = new ArrayList<>(); + private void updatePrivacyIconSlots() { if (getChipEnabled()) { if (mMicCameraIndicatorsEnabled) { - ignored.add(mView.getResources().getString( - com.android.internal.R.string.status_bar_camera)); - ignored.add(mView.getResources().getString( - com.android.internal.R.string.status_bar_microphone)); + mIconContainer.addIgnoredSlot(mCameraSlot); + mIconContainer.addIgnoredSlot(mMicSlot); + } else { + mIconContainer.removeIgnoredSlot(mCameraSlot); + mIconContainer.removeIgnoredSlot(mMicSlot); } if (mLocationIndicatorsEnabled) { - ignored.add(mView.getResources().getString( - com.android.internal.R.string.status_bar_location)); + mIconContainer.addIgnoredSlot(mLocationSlot); + } else { + mIconContainer.removeIgnoredSlot(mLocationSlot); } + } else { + mIconContainer.removeIgnoredSlot(mCameraSlot); + mIconContainer.removeIgnoredSlot(mMicSlot); + mIconContainer.removeIgnoredSlot(mLocationSlot); } - return ignored; } private boolean getChipEnabled() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java index d6fa21646402..32ac73375f1d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java @@ -25,6 +25,8 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.VisibleForTesting; + import com.android.settingslib.Utils; import com.android.settingslib.graph.SignalDrawable; import com.android.systemui.R; @@ -37,8 +39,10 @@ public class QSCarrier extends LinearLayout { private TextView mCarrierText; private ImageView mMobileSignal; private ImageView mMobileRoaming; + private View mSpacer; private CellSignalState mLastSignalState; private boolean mProviderModelInitialized = false; + private boolean mIsSingleCarrier; public QSCarrier(Context context) { super(context); @@ -63,18 +67,25 @@ public class QSCarrier extends LinearLayout { mMobileRoaming = findViewById(R.id.mobile_roaming); mMobileSignal = findViewById(R.id.mobile_signal); mCarrierText = findViewById(R.id.qs_carrier_text); + mSpacer = findViewById(R.id.spacer); } /** * Update the state of this view * @param state the current state of the signal for this view + * @param isSingleCarrier whether there is a single carrier being shown in the container * @return true if the state was actually changed */ - public boolean updateState(CellSignalState state) { - if (Objects.equals(state, mLastSignalState)) return false; + public boolean updateState(CellSignalState state, boolean isSingleCarrier) { + if (Objects.equals(state, mLastSignalState) && isSingleCarrier == mIsSingleCarrier) { + return false; + } mLastSignalState = state; - mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE); - if (state.visible) { + mIsSingleCarrier = isSingleCarrier; + final boolean visible = state.visible && !isSingleCarrier; + mMobileGroup.setVisibility(visible ? View.VISIBLE : View.GONE); + mSpacer.setVisibility(isSingleCarrier ? View.VISIBLE : View.GONE); + if (visible) { mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE); ColorStateList colorStateList = Utils.getColorAttr(mContext, android.R.attr.textColorPrimary); @@ -125,6 +136,11 @@ public class QSCarrier extends LinearLayout { com.android.settingslib.R.string.not_default_data_content_description)); } + @VisibleForTesting + View getRSSIView() { + return mMobileGroup; + } + public void setCarrierText(CharSequence text) { mCarrierText.setText(text); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java index f23c0580c409..67c4d33d53d3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java @@ -37,6 +37,7 @@ import com.android.keyguard.CarrierTextManager; import com.android.settingslib.AccessibilityContentDescriptions; import com.android.settingslib.mobile.TelephonyIcons; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -62,7 +63,8 @@ public class QSCarrierGroupController { private final NetworkController mNetworkController; private final CarrierTextManager mCarrierTextManager; private final TextView mNoSimTextView; - private final H mMainHandler; + // Non final for testing + private H mMainHandler; private final Callback mCallback; private boolean mListening; private final CellSignalState[] mInfos = @@ -74,6 +76,11 @@ public class QSCarrierGroupController { private final boolean mProviderModel; private final CarrierConfigTracker mCarrierConfigTracker; + private boolean mIsSingleCarrier; + private OnSingleCarrierChangedListener mOnSingleCarrierChangedListener; + + private final SlotIndexResolver mSlotIndexResolver; + private final NetworkController.SignalCallback mSignalCallback = new NetworkController.SignalCallback() { @Override @@ -207,7 +214,8 @@ public class QSCarrierGroupController { @Background Handler bgHandler, @Main Looper mainLooper, NetworkController networkController, CarrierTextManager.Builder carrierTextManagerBuilder, Context context, - CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags) { + CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags, + SlotIndexResolver slotIndexResolver) { if (featureFlags.isCombinedStatusBarSignalIconsEnabled()) { mProviderModel = true; @@ -222,6 +230,7 @@ public class QSCarrierGroupController { .setShowMissingSim(false) .build(); mCarrierConfigTracker = carrierConfigTracker; + mSlotIndexResolver = slotIndexResolver; View.OnClickListener onClickListener = v -> { if (!v.isVisibleToUser()) { return; @@ -256,6 +265,7 @@ public class QSCarrierGroupController { .toString(); mCarrierGroups[i].setOnClickListener(onClickListener); } + mIsSingleCarrier = computeIsSingleCarrier(); view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @@ -272,10 +282,24 @@ public class QSCarrierGroupController { @VisibleForTesting protected int getSlotIndex(int subscriptionId) { - return SubscriptionManager.getSlotIndex(subscriptionId); + return mSlotIndexResolver.getSlotIndex(subscriptionId); + } + + /** + * Sets a {@link OnSingleCarrierChangedListener}. + * + * This will get notified when the number of carriers changes between 1 and "not one". + * @param listener + */ + public void setOnSingleCarrierChangedListener(OnSingleCarrierChangedListener listener) { + mOnSingleCarrierChangedListener = listener; + } + + public boolean isSingleCarrier() { + return mIsSingleCarrier; } - private boolean isSingleCarrier() { + private boolean computeIsSingleCarrier() { int carrierCount = 0; for (int i = 0; i < SIM_SLOTS; i++) { @@ -315,7 +339,9 @@ public class QSCarrierGroupController { return; } - if (isSingleCarrier()) { + boolean singleCarrier = computeIsSingleCarrier(); + + if (singleCarrier) { for (int i = 0; i < SIM_SLOTS; i++) { if (mInfos[i].visible && mInfos[i].mobileSignalIconId == R.drawable.ic_qs_sim_card) { @@ -326,7 +352,7 @@ public class QSCarrierGroupController { } for (int i = 0; i < SIM_SLOTS; i++) { - mCarrierGroups[i].updateState(mInfos[i]); + mCarrierGroups[i].updateState(mInfos[i], singleCarrier); } mCarrierDividers[0].setVisibility( @@ -337,6 +363,12 @@ public class QSCarrierGroupController { mCarrierDividers[1].setVisibility( (mInfos[1].visible && mInfos[2].visible) || (mInfos[0].visible && mInfos[2].visible) ? View.VISIBLE : View.GONE); + if (mIsSingleCarrier != singleCarrier) { + mIsSingleCarrier = singleCarrier; + if (mOnSingleCarrierChangedListener != null) { + mOnSingleCarrierChangedListener.onSingleCarrierChanged(singleCarrier); + } + } } @MainThread @@ -433,12 +465,14 @@ public class QSCarrierGroupController { private final Context mContext; private final CarrierConfigTracker mCarrierConfigTracker; private final FeatureFlags mFeatureFlags; + private final SlotIndexResolver mSlotIndexResolver; @Inject public Builder(ActivityStarter activityStarter, @Background Handler handler, @Main Looper looper, NetworkController networkController, CarrierTextManager.Builder carrierTextControllerBuilder, Context context, - CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags) { + CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags, + SlotIndexResolver slotIndexResolver) { mActivityStarter = activityStarter; mHandler = handler; mLooper = looper; @@ -447,6 +481,7 @@ public class QSCarrierGroupController { mContext = context; mCarrierConfigTracker = carrierConfigTracker; mFeatureFlags = featureFlags; + mSlotIndexResolver = slotIndexResolver; } public Builder setQSCarrierGroup(QSCarrierGroup view) { @@ -457,7 +492,43 @@ public class QSCarrierGroupController { public QSCarrierGroupController build() { return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper, mNetworkController, mCarrierTextControllerBuilder, mContext, - mCarrierConfigTracker, mFeatureFlags); + mCarrierConfigTracker, mFeatureFlags, mSlotIndexResolver); + } + } + + /** + * Notify when the state changes from 1 carrier to "not one" and viceversa + */ + @FunctionalInterface + public interface OnSingleCarrierChangedListener { + void onSingleCarrierChanged(boolean isSingleCarrier); + } + + /** + * Interface for resolving slot index from subscription ID. + */ + @FunctionalInterface + public interface SlotIndexResolver { + /** + * Get slot index for given sub id. + */ + int getSlotIndex(int subscriptionId); + } + + /** + * Default implementation for {@link SlotIndexResolver}. + * + * It retrieves the slot index using {@link SubscriptionManager#getSlotIndex}. + */ + @SysUISingleton + public static class SubscriptionManagerSlotIndexResolver implements SlotIndexResolver { + + @Inject + public SubscriptionManagerSlotIndexResolver() {} + + @Override + public int getSlotIndex(int subscriptionId) { + return SubscriptionManager.getSlotIndex(subscriptionId); } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java index 34aac492dc30..4d633492ed76 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java @@ -33,6 +33,7 @@ import com.android.systemui.qs.QSFragment; import com.android.systemui.qs.QSPanel; import com.android.systemui.qs.QuickQSPanel; import com.android.systemui.qs.QuickStatusBarHeader; +import com.android.systemui.qs.carrier.QSCarrierGroupController; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.statusbar.phone.MultiUserSwitch; @@ -146,4 +147,8 @@ public interface QSFragmentModule { return useQsMediaPlayer(context); } + /** */ + @Binds + QSCarrierGroupController.SlotIndexResolver provideSlotIndexResolver( + QSCarrierGroupController.SubscriptionManagerSlotIndexResolver impl); }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java index de3be78e5463..6d1bbeed5372 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java @@ -89,5 +89,4 @@ public interface QSModule { /** */ @Binds QSHost provideQsHost(QSTileHost controllerImpl); - } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java index 1d791f5d632c..d262412d5182 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java @@ -40,6 +40,7 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.broadcast.BroadcastDispatcher; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -204,9 +205,14 @@ public class TileLifecycleManager extends BroadcastReceiver implements if (DEBUG) Log.d(TAG, "Unbinding service " + mIntent + " " + mUser); // Give it another chance next time it needs to be bound, out of kindness. mBindTryCount = 0; - mWrapper = null; + freeWrapper(); if (mIsBound) { - mContext.unbindService(this); + try { + mContext.unbindService(this); + } catch (Exception e) { + Log.e(TAG, "Failed to unbind service " + + mIntent.getComponent().flattenToShortString(), e); + } mIsBound = false; } } @@ -290,7 +296,9 @@ public class TileLifecycleManager extends BroadcastReceiver implements private void handleDeath() { if (mWrapper == null) return; - mWrapper = null; + freeWrapper(); + // Clearly not bound anymore + mIsBound = false; if (!mBound) return; if (DEBUG) Log.d(TAG, "handleDeath"); if (checkComponentState()) { @@ -472,6 +480,18 @@ public class TileLifecycleManager extends BroadcastReceiver implements return mToken; } + private void freeWrapper() { + if (mWrapper != null) { + try { + mWrapper.asBinder().unlinkToDeath(this, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "Trying to unlink not linked recipient for component" + + mIntent.getComponent().flattenToShortString()); + } + mWrapper = null; + } + } + public interface TileChangeListener { void onTileChanged(ComponentName tile); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java index 45c71744e0ec..fa2d4447f26d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles; import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; +import static android.os.UserManager.DISALLOW_CAMERA_TOGGLE; import static com.android.systemui.DejankUtils.whitelistIpcs; @@ -86,4 +87,9 @@ public class CameraToggleTile extends SensorPrivacyToggleTile { public @Sensor int getSensorId() { return CAMERA; } + + @Override + public String getRestriction() { + return DISALLOW_CAMERA_TOGGLE; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java index 48a1ad673d76..f4f0b2cdc432 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles; import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; +import static android.os.UserManager.DISALLOW_MICROPHONE_TOGGLE; import static com.android.systemui.DejankUtils.whitelistIpcs; @@ -86,4 +87,9 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile { public @Sensor int getSensorId() { return MICROPHONE; } + + @Override + public String getRestriction() { + return DISALLOW_MICROPHONE_TOGGLE; + } } 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 ab81ac1fd577..82b6c0c1805d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -153,25 +153,9 @@ 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 = getServiceLabelSafe(); + CharSequence label = mController.getWalletClient().getServiceLabel(); 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/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java index bf72b7728232..b0a1b18a8cd5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java @@ -22,6 +22,7 @@ import android.content.Intent; import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.os.Handler; import android.os.Looper; +import android.provider.Settings; import android.service.quicksettings.Tile; import android.view.View; import android.widget.Switch; @@ -63,6 +64,11 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS */ public abstract @DrawableRes int getIconRes(boolean isBlocked); + /** + * @return the user restriction name + */ + public abstract String getRestriction(); + protected SensorPrivacyToggleTile(QSHost host, @Background Looper backgroundLooper, @Main Handler mainHandler, @@ -103,6 +109,8 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS boolean isBlocked = arg == null ? mSensorPrivacyController.isSensorBlocked(getSensorId()) : (boolean) arg; + checkIfRestrictionEnforcedByAdminOnly(state, getRestriction()); + state.icon = ResourceIcon.get(getIconRes(isBlocked)); state.state = isBlocked ? Tile.STATE_INACTIVE : Tile.STATE_ACTIVE; state.value = !isBlocked; @@ -112,7 +120,6 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS } else { state.secondaryLabel = mContext.getString(R.string.quick_settings_camera_mic_available); } - state.handlesLongClick = false; state.contentDescription = state.label; state.expandedAccessibilityClassName = Switch.class.getName(); } @@ -124,7 +131,7 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS @Override public Intent getLongClickIntent() { - return null; + return new Intent(Settings.ACTION_PRIVACY_SETTINGS); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java deleted file mode 100644 index 310ecbc01bf0..000000000000 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java +++ /dev/null @@ -1,586 +0,0 @@ -/* - * Copyright (C) 2018 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.recents; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; - -import static com.android.systemui.Prefs.Key.DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT; -import static com.android.systemui.Prefs.Key.HAS_DISMISSED_RECENTS_QUICK_SCRUB_ONBOARDING_ONCE; -import static com.android.systemui.Prefs.Key.HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING; -import static com.android.systemui.Prefs.Key.HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING; -import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_COUNT; -import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_FROM_HOME_COUNT; -import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS; -import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP; -import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP; -import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE; - -import android.annotation.StringRes; -import android.annotation.TargetApi; -import android.app.ActivityManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.CornerPathEffect; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.drawable.ShapeDrawable; -import android.os.Build; -import android.os.RemoteException; -import android.os.SystemProperties; -import android.os.UserManager; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.widget.ImageView; -import android.widget.TextView; - -import com.android.systemui.Dependency; -import com.android.systemui.Prefs; -import com.android.systemui.R; -import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.shared.recents.IOverviewProxy; -import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.shared.system.TaskStackChangeListener; -import com.android.systemui.shared.system.TaskStackChangeListeners; - -import java.io.PrintWriter; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** - * Shows onboarding for the new recents interaction in P (codenamed quickstep). - */ -@TargetApi(Build.VERSION_CODES.P) -public class RecentsOnboarding { - - private static final String TAG = "RecentsOnboarding"; - private static final boolean RESET_PREFS_FOR_DEBUG = false; - private static final boolean ONBOARDING_ENABLED = true; - private static final long SHOW_DELAY_MS = 500; - private static final long SHOW_DURATION_MS = 300; - private static final long HIDE_DURATION_MS = 100; - // Show swipe-up tips after opening overview from home this number of times. - private static final int SWIPE_UP_SHOW_ON_OVERVIEW_OPENED_FROM_HOME_COUNT = 3; - // Show quick scrub tips after opening overview this number of times. - private static final int QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT = 10; - // Maximum number of dismissals while still showing swipe-up tips. - private static final int MAX_DISMISSAL_ON_SWIPE_UP_SHOW = 2; - // Number of dismissals for swipe-up tips when exponential backoff starts. - private static final int BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW = 1; - // After explicitly dismissing for <= BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW times, show again - // after launching this number of apps for swipe-up tips. - private static final int SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS = 5; - // After explicitly dismissing for > BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW but - // <= MAX_DISMISSAL_ON_SWIPE_UP_SHOW times, show again after launching this number of apps for - // swipe-up tips. - private static final int SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS_BACK_OFF = 40; - - private final Context mContext; - private final WindowManager mWindowManager; - private final OverviewProxyService mOverviewProxyService; - private Set<String> mBlacklistedPackages; - private final View mLayout; - private final TextView mTextView; - private final ImageView mDismissView; - private final View mArrowView; - private final int mOnboardingToastColor; - private final int mOnboardingToastArrowRadius; - private int mNavBarHeight; - private int mNavBarMode = NAV_BAR_MODE_3BUTTON; - - private boolean mOverviewProxyListenerRegistered; - private boolean mTaskListenerRegistered; - private boolean mLayoutAttachedToWindow; - private boolean mHasDismissedSwipeUpTip; - private boolean mHasDismissedQuickScrubTip; - private int mNumAppsLaunchedSinceSwipeUpTipDismiss; - private int mOverviewOpenedCountSinceQuickScrubTipDismiss; - - private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() { - private String mLastPackageName; - - @Override - public void onTaskCreated(int taskId, ComponentName componentName) { - onAppLaunch(); - } - - @Override - public void onTaskMovedToFront(int taskId) { - onAppLaunch(); - } - - private void onAppLaunch() { - ActivityManager.RunningTaskInfo info = ActivityManagerWrapper.getInstance() - .getRunningTask(); - if (info == null || info.baseActivity == null) { - return; - } - if (mBlacklistedPackages.contains(info.baseActivity.getPackageName())) { - hide(true); - return; - } - if (info.baseActivity.getPackageName().equals(mLastPackageName)) { - return; - } - mLastPackageName = info.baseActivity.getPackageName(); - int activityType = info.configuration.windowConfiguration.getActivityType(); - if (activityType == ACTIVITY_TYPE_STANDARD) { - boolean alreadySeenSwipeUpOnboarding = hasSeenSwipeUpOnboarding(); - boolean alreadySeenQuickScrubsOnboarding = hasSeenQuickScrubOnboarding(); - if (alreadySeenSwipeUpOnboarding && alreadySeenQuickScrubsOnboarding) { - onDisconnectedFromLauncher(); - return; - } - - boolean shouldLog = false; - if (!alreadySeenSwipeUpOnboarding) { - if (getOpenedOverviewFromHomeCount() - >= SWIPE_UP_SHOW_ON_OVERVIEW_OPENED_FROM_HOME_COUNT) { - if (mHasDismissedSwipeUpTip) { - int hasDimissedSwipeUpOnboardingCount = - getDismissedSwipeUpOnboardingCount(); - if (hasDimissedSwipeUpOnboardingCount - > MAX_DISMISSAL_ON_SWIPE_UP_SHOW) { - return; - } - final int swipeUpShowOnAppLauncherAfterDismiss = - hasDimissedSwipeUpOnboardingCount - <= BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW - ? SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS - : SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS_BACK_OFF; - mNumAppsLaunchedSinceSwipeUpTipDismiss++; - if (mNumAppsLaunchedSinceSwipeUpTipDismiss - >= swipeUpShowOnAppLauncherAfterDismiss) { - mNumAppsLaunchedSinceSwipeUpTipDismiss = 0; - shouldLog = show(R.string.recents_swipe_up_onboarding); - } - } else { - shouldLog = show(R.string.recents_swipe_up_onboarding); - } - if (shouldLog) { - notifyOnTip(VISIBLE, RECENTS_SWIPE_UP_ONBOARDING_TIP); - } - } - } else { - if (getOpenedOverviewCount() >= QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT) { - if (mHasDismissedQuickScrubTip) { - if (mOverviewOpenedCountSinceQuickScrubTipDismiss - >= QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT) { - mOverviewOpenedCountSinceQuickScrubTipDismiss = 0; - shouldLog = show(R.string.recents_quick_scrub_onboarding); - } - } else { - shouldLog = show(R.string.recents_quick_scrub_onboarding); - } - if (shouldLog) { - notifyOnTip(VISIBLE, RECENTS_QUICK_SCRUB_ONBOARDING_TIP); - } - } - - } - } else { - hide(false); - } - } - }; - - private OverviewProxyService.OverviewProxyListener mOverviewProxyListener = - new OverviewProxyService.OverviewProxyListener() { - @Override - public void onOverviewShown(boolean fromHome) { - if (!hasSeenSwipeUpOnboarding() && !fromHome) { - setHasSeenSwipeUpOnboarding(true); - } - if (fromHome) { - incrementOpenedOverviewFromHomeCount(); - } - incrementOpenedOverviewCount(); - - if (getOpenedOverviewCount() >= QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT) { - if (mHasDismissedQuickScrubTip) { - mOverviewOpenedCountSinceQuickScrubTipDismiss++; - } - } - } - - @Override - public void onQuickStepStarted() { - hide(true); - } - - @Override - public void onQuickScrubStarted() { - boolean alreadySeenQuickScrubsOnboarding = hasSeenQuickScrubOnboarding(); - if (!alreadySeenQuickScrubsOnboarding) { - setHasSeenQuickScrubOnboarding(true); - } - } - }; - - private final View.OnAttachStateChangeListener mOnAttachStateChangeListener - = new View.OnAttachStateChangeListener() { - - private final BroadcastDispatcher mBroadcastDispatcher = Dependency.get( - BroadcastDispatcher.class); - - @Override - public void onViewAttachedToWindow(View view) { - if (view == mLayout) { - mBroadcastDispatcher.registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_SCREEN_OFF)); - mLayoutAttachedToWindow = true; - if (view.getTag().equals(R.string.recents_swipe_up_onboarding)) { - mHasDismissedSwipeUpTip = false; - } else { - mHasDismissedQuickScrubTip = false; - } - } - } - - @Override - public void onViewDetachedFromWindow(View view) { - if (view == mLayout) { - mLayoutAttachedToWindow = false; - if (view.getTag().equals(R.string.recents_quick_scrub_onboarding)) { - mHasDismissedQuickScrubTip = true; - if (hasDismissedQuickScrubOnboardingOnce()) { - // If user dismisses the quick scrub tip twice, we consider user has seen it - // and do not show it again. - setHasSeenQuickScrubOnboarding(true); - } else { - setHasDismissedQuickScrubOnboardingOnce(true); - } - mOverviewOpenedCountSinceQuickScrubTipDismiss = 0; - } - mBroadcastDispatcher.unregisterReceiver(mReceiver); - } - } - }; - - public RecentsOnboarding(Context context, OverviewProxyService overviewProxyService) { - mContext = context; - mOverviewProxyService = overviewProxyService; - final Resources res = context.getResources(); - mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - mBlacklistedPackages = new HashSet<>(); - Collections.addAll(mBlacklistedPackages, res.getStringArray( - R.array.recents_onboarding_blacklisted_packages)); - mLayout = LayoutInflater.from(mContext).inflate(R.layout.recents_onboarding, null); - mTextView = mLayout.findViewById(R.id.onboarding_text); - mDismissView = mLayout.findViewById(R.id.dismiss); - mArrowView = mLayout.findViewById(R.id.arrow); - - TypedValue typedValue = new TypedValue(); - context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true); - mOnboardingToastColor = res.getColor(typedValue.resourceId); - mOnboardingToastArrowRadius = res.getDimensionPixelSize( - R.dimen.recents_onboarding_toast_arrow_corner_radius); - - mLayout.addOnAttachStateChangeListener(mOnAttachStateChangeListener); - mDismissView.setOnClickListener(v -> { - hide(true); - if (v.getTag().equals(R.string.recents_swipe_up_onboarding)) { - mHasDismissedSwipeUpTip = true; - mNumAppsLaunchedSinceSwipeUpTipDismiss = 0; - setDismissedSwipeUpOnboardingCount(getDismissedSwipeUpOnboardingCount() + 1); - if (getDismissedSwipeUpOnboardingCount() > MAX_DISMISSAL_ON_SWIPE_UP_SHOW) { - setHasSeenSwipeUpOnboarding(true); - } - notifyOnTip(DISMISS, RECENTS_SWIPE_UP_ONBOARDING_TIP); - } else { - notifyOnTip(DISMISS, RECENTS_QUICK_SCRUB_ONBOARDING_TIP); - } - }); - - ViewGroup.LayoutParams arrowLp = mArrowView.getLayoutParams(); - ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create( - arrowLp.width, arrowLp.height, false)); - Paint arrowPaint = arrowDrawable.getPaint(); - arrowPaint.setColor(mOnboardingToastColor); - // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable. - arrowPaint.setPathEffect(new CornerPathEffect(mOnboardingToastArrowRadius)); - mArrowView.setBackground(arrowDrawable); - - if (RESET_PREFS_FOR_DEBUG) { - setHasSeenSwipeUpOnboarding(false); - setHasSeenQuickScrubOnboarding(false); - setDismissedSwipeUpOnboardingCount(0); - setHasDismissedQuickScrubOnboardingOnce(false); - setOpenedOverviewCount(0); - setOpenedOverviewFromHomeCount(0); - } - } - - private void notifyOnTip(int action, int target) { - try { - IOverviewProxy overviewProxy = mOverviewProxyService.getProxy(); - if (overviewProxy != null) { - overviewProxy.onTip(action, target); - } - } catch (RemoteException e) { - } - } - - public void onNavigationModeChanged(int mode) { - mNavBarMode = mode; - } - - public void onConnectedToLauncher() { - if (!ONBOARDING_ENABLED || QuickStepContract.isGesturalMode(mNavBarMode)) { - return; - } - - if (hasSeenSwipeUpOnboarding() && hasSeenQuickScrubOnboarding()) { - return; - } - - if (!mOverviewProxyListenerRegistered) { - mOverviewProxyService.addCallback(mOverviewProxyListener); - mOverviewProxyListenerRegistered = true; - } - if (!mTaskListenerRegistered) { - TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskListener); - mTaskListenerRegistered = true; - } - } - - public void onDisconnectedFromLauncher() { - - if (mOverviewProxyListenerRegistered) { - mOverviewProxyService.removeCallback(mOverviewProxyListener); - mOverviewProxyListenerRegistered = false; - } - if (mTaskListenerRegistered) { - TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskListener); - mTaskListenerRegistered = false; - } - - mHasDismissedSwipeUpTip = false; - mHasDismissedQuickScrubTip = false; - mNumAppsLaunchedSinceSwipeUpTipDismiss = 0; - mOverviewOpenedCountSinceQuickScrubTipDismiss = 0; - hide(true); - } - - public void onConfigurationChanged(Configuration newConfiguration) { - if (newConfiguration.orientation != Configuration.ORIENTATION_PORTRAIT) { - hide(false); - } - } - - public boolean show(@StringRes int stringRes) { - if (!shouldShow()) { - return false; - } - mDismissView.setTag(stringRes); - mLayout.setTag(stringRes); - mTextView.setText(stringRes); - // Only show in portrait. - int orientation = mContext.getResources().getConfiguration().orientation; - if (!mLayoutAttachedToWindow && orientation == Configuration.ORIENTATION_PORTRAIT) { - mLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - - final int gravity; - final int x; - if (stringRes == R.string.recents_swipe_up_onboarding) { - gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; - x = 0; - } else { - int layoutDirection = - mContext.getResources().getConfiguration().getLayoutDirection(); - gravity = Gravity.BOTTOM | (layoutDirection == View.LAYOUT_DIRECTION_LTR - ? Gravity.LEFT : Gravity.RIGHT); - x = mContext.getResources().getDimensionPixelSize( - R.dimen.recents_quick_scrub_onboarding_margin_start); - } - mWindowManager.addView(mLayout, getWindowLayoutParams(gravity, x)); - mLayout.setAlpha(0); - mLayout.animate() - .alpha(1f) - .withLayer() - .setStartDelay(SHOW_DELAY_MS) - .setDuration(SHOW_DURATION_MS) - .setInterpolator(new DecelerateInterpolator()) - .start(); - return true; - } - return false; - } - - /** - * @return True unless setprop has been set to false, we're in demo mode, or running tests in - * automation. - */ - private boolean shouldShow() { - return SystemProperties.getBoolean("persist.quickstep.onboarding.enabled", - !(mContext.getSystemService(UserManager.class)).isDemoUser() && - !ActivityManager.isRunningInTestHarness()); - } - - public void hide(boolean animate) { - if (mLayoutAttachedToWindow) { - if (animate) { - mLayout.animate() - .alpha(0f) - .withLayer() - .setStartDelay(0) - .setDuration(HIDE_DURATION_MS) - .setInterpolator(new AccelerateInterpolator()) - .withEndAction(() -> mWindowManager.removeViewImmediate(mLayout)) - .start(); - } else { - mLayout.animate().cancel(); - mWindowManager.removeViewImmediate(mLayout); - } - } - } - - public void setNavBarHeight(int navBarHeight) { - mNavBarHeight = navBarHeight; - } - - public void dump(PrintWriter pw) { - pw.println("RecentsOnboarding"); - pw.println(" mTaskListenerRegistered: " + mTaskListenerRegistered); - pw.println(" mOverviewProxyListenerRegistered: " + mOverviewProxyListenerRegistered); - pw.println(" mLayoutAttachedToWindow: " + mLayoutAttachedToWindow); - pw.println(" mHasDismissedSwipeUpTip: " + mHasDismissedSwipeUpTip); - pw.println(" mHasDismissedQuickScrubTip: " + mHasDismissedQuickScrubTip); - pw.println(" mNumAppsLaunchedSinceSwipeUpTipDismiss: " - + mNumAppsLaunchedSinceSwipeUpTipDismiss); - pw.println(" hasSeenSwipeUpOnboarding: " + hasSeenSwipeUpOnboarding()); - pw.println(" hasSeenQuickScrubOnboarding: " + hasSeenQuickScrubOnboarding()); - pw.println(" getDismissedSwipeUpOnboardingCount: " - + getDismissedSwipeUpOnboardingCount()); - pw.println(" hasDismissedQuickScrubOnboardingOnce: " - + hasDismissedQuickScrubOnboardingOnce()); - pw.println(" getOpenedOverviewCount: " + getOpenedOverviewCount()); - pw.println(" getOpenedOverviewFromHomeCount: " + getOpenedOverviewFromHomeCount()); - } - - private WindowManager.LayoutParams getWindowLayoutParams(int gravity, int x) { - int flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT, - x, -mNavBarHeight / 2, - WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, - flags, - PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; - lp.setTitle("RecentsOnboarding"); - lp.gravity = gravity; - return lp; - } - - private boolean hasSeenSwipeUpOnboarding() { - return Prefs.getBoolean(mContext, HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING, false); - } - - private void setHasSeenSwipeUpOnboarding(boolean hasSeenSwipeUpOnboarding) { - Prefs.putBoolean(mContext, HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING, hasSeenSwipeUpOnboarding); - if (hasSeenSwipeUpOnboarding && hasSeenQuickScrubOnboarding()) { - onDisconnectedFromLauncher(); - } - } - - private boolean hasSeenQuickScrubOnboarding() { - return Prefs.getBoolean(mContext, HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING, false); - } - - private void setHasSeenQuickScrubOnboarding(boolean hasSeenQuickScrubOnboarding) { - Prefs.putBoolean(mContext, HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING, - hasSeenQuickScrubOnboarding); - if (hasSeenQuickScrubOnboarding && hasSeenSwipeUpOnboarding()) { - onDisconnectedFromLauncher(); - } - } - - private int getDismissedSwipeUpOnboardingCount() { - return Prefs.getInt(mContext, DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT, 0); - } - - private void setDismissedSwipeUpOnboardingCount(int dismissedSwipeUpOnboardingCount) { - Prefs.putInt(mContext, DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT, - dismissedSwipeUpOnboardingCount); - } - - private boolean hasDismissedQuickScrubOnboardingOnce() { - return Prefs.getBoolean(mContext, HAS_DISMISSED_RECENTS_QUICK_SCRUB_ONBOARDING_ONCE, false); - } - - private void setHasDismissedQuickScrubOnboardingOnce( - boolean hasDismissedQuickScrubOnboardingOnce) { - Prefs.putBoolean(mContext, HAS_DISMISSED_RECENTS_QUICK_SCRUB_ONBOARDING_ONCE, - hasDismissedQuickScrubOnboardingOnce); - } - - private int getOpenedOverviewFromHomeCount() { - return Prefs.getInt(mContext, OVERVIEW_OPENED_FROM_HOME_COUNT, 0); - } - - private void incrementOpenedOverviewFromHomeCount() { - int openedOverviewFromHomeCount = getOpenedOverviewFromHomeCount(); - if (openedOverviewFromHomeCount >= SWIPE_UP_SHOW_ON_OVERVIEW_OPENED_FROM_HOME_COUNT) { - return; - } - setOpenedOverviewFromHomeCount(openedOverviewFromHomeCount + 1); - } - - private void setOpenedOverviewFromHomeCount(int openedOverviewFromHomeCount) { - Prefs.putInt(mContext, OVERVIEW_OPENED_FROM_HOME_COUNT, openedOverviewFromHomeCount); - } - - private int getOpenedOverviewCount() { - return Prefs.getInt(mContext, OVERVIEW_OPENED_COUNT, 0); - } - - private void incrementOpenedOverviewCount() { - int openedOverviewCount = getOpenedOverviewCount(); - if (openedOverviewCount >= QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT) { - return; - } - setOpenedOverviewCount(openedOverviewCount + 1); - } - - private void setOpenedOverviewCount(int openedOverviewCount) { - Prefs.putInt(mContext, OVERVIEW_OPENED_COUNT, openedOverviewCount); - } - - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { - hide(false); - } - } - }; -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index aafeabc7c1a2..dce19cf86b35 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt @@ -45,6 +45,8 @@ open class BlurUtils @Inject constructor( val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius) val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius) + private var lastAppliedBlur = 0 + init { dumpManager.registerDumpable(javaClass.name, this) } @@ -75,17 +77,25 @@ open class BlurUtils @Inject constructor( * * @param viewRootImpl The window root. * @param radius blur radius in pixels. + * @param opaque if surface is opaque, regardless or having blurs or no. */ - fun applyBlur(viewRootImpl: ViewRootImpl?, radius: Int, opaqueBackground: Boolean) { + fun applyBlur(viewRootImpl: ViewRootImpl?, radius: Int, opaque: Boolean) { if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid || !supportsBlursOnWindows()) { return } createTransaction().use { it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius) - it.setOpaque(viewRootImpl.surfaceControl, opaqueBackground) + it.setOpaque(viewRootImpl.surfaceControl, opaque) + if (lastAppliedBlur == 0 && radius != 0) { + it.setEarlyWakeupStart() + } + if (lastAppliedBlur != 0 && radius == 0) { + it.setEarlyWakeupEnd() + } it.apply() } + lastAppliedBlur = radius } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 44399a126624..120121ce7f0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -94,7 +94,7 @@ import javax.inject.Inject; * Controls the indications and error messages shown on the Keyguard */ @SysUISingleton -public class KeyguardIndicationController implements KeyguardStateController.Callback { +public class KeyguardIndicationController { private static final String TAG = "KeyguardIndication"; private static final boolean DEBUG_CHARGING_SPEED = false; @@ -206,7 +206,7 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback()); mKeyguardUpdateMonitor.registerCallback(mTickReceiver); mStatusBarStateController.addCallback(mStatusBarStateListener); - mKeyguardStateController.addCallback(this); + mKeyguardStateController.addCallback(mKeyguardStateCallback); mStatusBarStateListener.onDozingChanged(mStatusBarStateController.isDozing()); } @@ -827,11 +827,6 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal mRotateTextViewController.dump(fd, pw, args); } - @Override - public void onUnlockedChanged() { - updateIndication(false); - } - protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback { public static final int HIDE_DELAY_MS = 5000; @@ -967,10 +962,8 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal } private boolean shouldSuppressFaceMsgAndShowTryFingerprintMsg() { - // For dual biometric, don't show face auth messages unless face auth was explicitly - // requested by the user. + // For dual biometric, don't show face auth messages return mKeyguardUpdateMonitor.isFingerprintDetectionRunning() - && !mKeyguardUpdateMonitor.isFaceAuthUserRequested() && mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed( true /* isStrongBiometric */); } @@ -1068,4 +1061,20 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal updateIndication(false); } }; + + private KeyguardStateController.Callback mKeyguardStateCallback = + new KeyguardStateController.Callback() { + @Override + public void onUnlockedChanged() { + updateIndication(false); + } + + @Override + public void onKeyguardShowingChanged() { + if (!mKeyguardStateController.isShowing()) { + mTopIndicationView.clearMessages(); + mLockScreenIndicationView.clearMessages(); + } + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 28bdd5fbeb8b..db553e4b093b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -89,6 +89,9 @@ class NotificationShadeDepthController @Inject constructor( private var prevShadeDirection = 0 private var prevShadeVelocity = 0f + // Only for dumpsys + private var lastAppliedBlur = 0 + @VisibleForTesting var shadeSpring = DepthAnimation() var shadeAnimation = DepthAnimation() @@ -201,6 +204,7 @@ class NotificationShadeDepthController @Inject constructor( val opaque = scrimsVisible && !blursDisabledForAppLaunch Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur) blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur, opaque) + lastAppliedBlur = blur try { if (root.isAttachedToWindow && root.windowToken != null) { wallpaperManager.setWallpaperZoomOut(root.windowToken, zoomOut) @@ -271,6 +275,11 @@ class NotificationShadeDepthController @Inject constructor( brightnessMirrorSpring.finishIfRunning() } } + + override fun onDozeAmountChanged(linear: Float, eased: Float) { + wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(eased) + scheduleUpdate() + } } init { @@ -428,6 +437,9 @@ class NotificationShadeDepthController @Inject constructor( it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}") it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius") it.println("blursDisabledForAppLaunch: $blursDisabledForAppLaunch") + it.println("qsPanelExpansion: $qsPanelExpansion") + it.println("transitionToFullShadeProgress: $transitionToFullShadeProgress") + it.println("lastAppliedBlur: $lastAppliedBlur") } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java index b6aed23e64ee..180f81c22a9d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java @@ -131,7 +131,7 @@ public class RemoteInputController { */ public void removeRemoteInput(NotificationEntry entry, Object token) { Objects.requireNonNull(entry); - if (entry.mRemoteEditImeVisible) return; + if (entry.mRemoteEditImeVisible && entry.mRemoteEditImeAnimatingAway) return; // If the view is being removed, this may be called even though we're not active if (!isRemoteInputActive(entry)) return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt index 5f10e557faed..29cfb07a14f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.events import android.animation.Animator import android.annotation.UiThread +import android.graphics.Point +import android.graphics.Rect import android.util.Log import android.view.Gravity import android.view.View @@ -31,9 +33,16 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.StatusBarState.SHADE import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED -import com.android.systemui.statusbar.phone.StatusBarLocationPublisher -import com.android.systemui.statusbar.phone.StatusBarMarginUpdatedListener +import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider +import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.leak.RotationUtils +import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE +import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE +import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE +import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN +import com.android.systemui.util.leak.RotationUtils.Rotation import java.lang.IllegalStateException import java.util.concurrent.Executor @@ -58,7 +67,8 @@ import javax.inject.Inject class PrivacyDotViewController @Inject constructor( @Main private val mainExecutor: Executor, private val stateController: StatusBarStateController, - private val locationPublisher: StatusBarLocationPublisher, + private val configurationController: ConfigurationController, + private val contentInsetsProvider: StatusBarContentInsetsProvider, private val animationScheduler: SystemStatusAnimationScheduler ) { private var sbHeightPortrait = 0 @@ -84,18 +94,27 @@ class PrivacyDotViewController @Inject constructor( // Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread private var uiExecutor: DelayableExecutor? = null - private val marginListener: StatusBarMarginUpdatedListener = - object : StatusBarMarginUpdatedListener { - override fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int) { - setStatusBarMargins(marginLeft, marginRight) - } - } - private val views: Sequence<View> get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl) init { - locationPublisher.addCallback(marginListener) + contentInsetsProvider.addCallback(object : StatusBarContentInsetsChangedListener { + override fun onStatusBarContentInsetsChanged() { + dlog("onStatusBarContentInsetsChanged: ") + setNewLayoutRects() + } + }) + configurationController.addCallback(object : ConfigurationController.ConfigurationListener { + override fun onLayoutDirectionChanged(isRtl: Boolean) { + synchronized(this) { + val corner = selectDesignatedCorner(nextViewState.rotation, isRtl) + nextViewState = nextViewState.copy( + layoutRtl = isRtl, + designatedCorner = corner + ) + } + } + }) stateController.addCallback(object : StatusBarStateController.StateListener { override fun onExpandedChanged(isExpanded: Boolean) { @@ -123,16 +142,19 @@ class PrivacyDotViewController @Inject constructor( fun setNewRotation(rot: Int) { dlog("updateRotation: $rot") + val isRtl: Boolean synchronized(lock) { if (rot == nextViewState.rotation) { return } + + isRtl = nextViewState.layoutRtl } // If we rotated, hide all dotes until the next state resolves setCornerVisibilities(View.INVISIBLE) - val newCorner = selectDesignatedCorner(rot) + val newCorner = selectDesignatedCorner(rot, isRtl) val index = newCorner.cornerIndex() val h = when (rot) { @@ -222,15 +244,77 @@ class PrivacyDotViewController @Inject constructor( } } + @UiThread + private fun setCornerSizes(state: ViewState) { + // StatusBarContentInsetsProvider can tell us the location of the privacy indicator dot + // in every rotation. The only thing we need to check is rtl + val rtl = state.layoutRtl + val size = Point() + tl.context.display.getRealSize(size) + val currentRotation = RotationUtils.getExactRotation(tl.context) + + val displayWidth: Int + val displayHeight: Int + if (currentRotation == ROTATION_LANDSCAPE || currentRotation == ROTATION_SEASCAPE) { + displayWidth = size.y + displayHeight = size.x + } else { + displayWidth = size.x + displayHeight = size.y + } + + var rot = activeRotationForCorner(tl, rtl) + var contentInsets = state.contentRectForRotation(rot) + (tl.layoutParams as FrameLayout.LayoutParams).apply { + height = contentInsets.height() + if (rtl) { + width = contentInsets.left + } else { + width = displayHeight - contentInsets.right + } + } + + rot = activeRotationForCorner(tr, rtl) + contentInsets = state.contentRectForRotation(rot) + (tr.layoutParams as FrameLayout.LayoutParams).apply { + height = contentInsets.height() + if (rtl) { + width = contentInsets.left + } else { + width = displayWidth - contentInsets.right + } + } + + rot = activeRotationForCorner(br, rtl) + contentInsets = state.contentRectForRotation(rot) + (br.layoutParams as FrameLayout.LayoutParams).apply { + height = contentInsets.height() + if (rtl) { + width = contentInsets.left + } else { + width = displayHeight - contentInsets.right + } + } + + rot = activeRotationForCorner(bl, rtl) + contentInsets = state.contentRectForRotation(rot) + (bl.layoutParams as FrameLayout.LayoutParams).apply { + height = contentInsets.height() + if (rtl) { + width = contentInsets.left + } else { + width = displayWidth - contentInsets.right + } + } + } + // Designated view will be the one at statusbar's view.END @UiThread - private fun selectDesignatedCorner(r: Int): View? { + private fun selectDesignatedCorner(r: Int, isRtl: Boolean): View? { if (!this::tl.isInitialized) { return null } - val isRtl = tl.isLayoutRtl - return when (r) { 0 -> if (isRtl) tl else tr 1 -> if (isRtl) tr else br @@ -282,6 +366,17 @@ class PrivacyDotViewController @Inject constructor( return modded } + @Rotation + private fun activeRotationForCorner(corner: View, rtl: Boolean): Int { + // Each corner will only be visible in a single rotation, based on rtl + return when (corner) { + tr -> if (rtl) ROTATION_LANDSCAPE else ROTATION_NONE + tl -> if (rtl) ROTATION_NONE else ROTATION_SEASCAPE + br -> if (rtl) ROTATION_UPSIDE_DOWN else ROTATION_LANDSCAPE + else /* bl */ -> if (rtl) ROTATION_SEASCAPE else ROTATION_UPSIDE_DOWN + } + } + private fun widthForCorner(corner: Int, left: Int, right: Int): Int { return when (corner) { TOP_LEFT, BOTTOM_LEFT -> left @@ -303,15 +398,32 @@ class PrivacyDotViewController @Inject constructor( bl = bottomLeft br = bottomRight - val dc = selectDesignatedCorner(0) + val rtl = configurationController.isLayoutRtl + val dc = selectDesignatedCorner(0, rtl) + val index = dc.cornerIndex() mainExecutor.execute { animationScheduler.addCallback(systemStatusAnimationCallback) } + val left = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_SEASCAPE) + val top = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_NONE) + val right = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_LANDSCAPE) + val bottom = contentInsetsProvider + .getStatusBarContentInsetsForRotation(ROTATION_UPSIDE_DOWN) + synchronized(lock) { - nextViewState = nextViewState.copy(designatedCorner = dc, cornerIndex = index) + nextViewState = nextViewState.copy( + viewInitialized = true, + designatedCorner = dc, + cornerIndex = index, + seascapeRect = left, + portraitRect = top, + landscapeRect = right, + upsideDownRect = bottom, + layoutRtl = rtl + ) } } @@ -324,19 +436,6 @@ class PrivacyDotViewController @Inject constructor( sbHeightLandscape = landscape } - /** - * The dot view containers will fill the margin in order to position the dots correctly - * - * @param left the space between the status bar contents and the left side of the screen - * @param right space between the status bar contents and the right side of the screen - */ - private fun setStatusBarMargins(left: Int, right: Int) { - dlog("setStatusBarMargins l=$left r=$right") - synchronized(lock) { - nextViewState = nextViewState.copy(marginLeft = left, marginRight = right) - } - } - private fun updateStatusBarState() { synchronized(lock) { nextViewState = nextViewState.copy(shadeExpanded = isShadeInQs()) @@ -377,6 +476,11 @@ class PrivacyDotViewController @Inject constructor( @UiThread private fun resolveState(state: ViewState) { dlog("resolveState $state") + if (!state.viewInitialized) { + dlog("resolveState: view is not initialized. skipping.") + return + } + if (state == currentViewState) { dlog("resolveState: skipping") return @@ -387,23 +491,15 @@ class PrivacyDotViewController @Inject constructor( updateRotations(state.rotation) } - if (state.height != currentViewState.height) { - updateHeights(state.rotation) - } - - if (state.marginLeft != currentViewState.marginLeft || - state.marginRight != currentViewState.marginRight) { - updateCornerSizes(state.marginLeft, state.marginRight, state.rotation) + if (state.needsLayout(currentViewState)) { + setCornerSizes(state) + views.forEach { it.requestLayout() } } if (state.designatedCorner != currentViewState.designatedCorner) { updateDesignatedCorner(state.designatedCorner, state.shouldShowDot()) } - if (state.needsLayout(currentViewState)) { - views.forEach { it.requestLayout() } - } - val shouldShow = state.shouldShowDot() if (shouldShow != currentViewState.shouldShowDot()) { if (shouldShow && state.designatedCorner != null) { @@ -441,6 +537,30 @@ class PrivacyDotViewController @Inject constructor( } return -1 } + + // Returns [left, top, right, bottom] aka [seascape, none, landscape, upside-down] + private fun getLayoutRects(): List<Rect> { + val left = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_SEASCAPE) + val top = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_NONE) + val right = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_LANDSCAPE) + val bottom = contentInsetsProvider + .getStatusBarContentInsetsForRotation(ROTATION_UPSIDE_DOWN) + + return listOf(left, top, right, bottom) + } + + private fun setNewLayoutRects() { + val rects = getLayoutRects() + + synchronized(lock) { + nextViewState = nextViewState.copy( + seascapeRect = rects[0], + portraitRect = rects[1], + landscapeRect = rects[2], + upsideDownRect = rects[3] + ) + } + } } private fun dlog(s: String) { @@ -461,7 +581,7 @@ const val BOTTOM_RIGHT = 2 const val BOTTOM_LEFT = 3 private const val DURATION = 160L private const val TAG = "PrivacyDotViewController" -private const val DEBUG = true +private const val DEBUG = false private const val DEBUG_VERBOSE = false private fun Int.toGravity(): Int { @@ -485,14 +605,20 @@ private fun Int.innerGravity(): Int { } private data class ViewState( + val viewInitialized: Boolean = false, + val systemPrivacyEventIsActive: Boolean = false, val shadeExpanded: Boolean = false, val qsExpanded: Boolean = false, + val portraitRect: Rect? = null, + val landscapeRect: Rect? = null, + val upsideDownRect: Rect? = null, + val seascapeRect: Rect? = null, + val layoutRtl: Boolean = false, + val rotation: Int = 0, val height: Int = 0, - val marginLeft: Int = 0, - val marginRight: Int = 0, val cornerIndex: Int = -1, val designatedCorner: View? = null ) { @@ -502,7 +628,20 @@ private data class ViewState( fun needsLayout(other: ViewState): Boolean { return rotation != other.rotation || - marginRight != other.marginRight || - height != other.height + layoutRtl != other.layoutRtl || + portraitRect != other.portraitRect || + landscapeRect != other.landscapeRect || + upsideDownRect != other.upsideDownRect || + seascapeRect != other.seascapeRect + } + + fun contentRectForRotation(@Rotation rot: Int): Rect { + return when (rot) { + ROTATION_NONE -> portraitRect!! + ROTATION_LANDSCAPE -> landscapeRect!! + ROTATION_UPSIDE_DOWN -> upsideDownRect!! + ROTATION_SEASCAPE -> seascapeRect!! + else -> throw IllegalArgumentException("not a rotation ($rot)") + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 96b0e7819c7a..94ee868ceebc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -183,6 +183,7 @@ public final class NotificationEntry extends ListEntry { private boolean mIsMarkedForUserTriggeredMovement; private boolean mIsAlerting; + public boolean mRemoteEditImeAnimatingAway; public boolean mRemoteEditImeVisible; private boolean mExpandAnimationRunning; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 93166f39ad62..73bb6cd9ba1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -287,7 +287,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mGroupExpansionChanging = true; final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry); boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry); - mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded); + mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded); MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded); onExpansionChanged(true /* userAction */, wasExpanded); @@ -310,7 +310,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView setUserExpanded(nowExpanded); } notifyHeightChanged(true); - mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded); + mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded); MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_EXPANDER, nowExpanded); } @@ -3064,7 +3064,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public interface OnExpandClickListener { - void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded); + void onExpandClicked(NotificationEntry clickedEntry, View clickedView, boolean nowExpanded); } @Override 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 f460a132d65c..d0507e1e136c 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 @@ -154,15 +154,21 @@ public class StackScrollAlgorithm { 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. + // After the shelf has updated its yTranslation, explicitly set alpha=0 for view below shelf + // to skip rendering them in the hardware layer. We do not set them invisible because that + // runs invalidate & onDraw when these views return onscreen, which is more expensive. final float shelfTop = shelf.getViewState().yTranslation; for (ExpandableView view : algorithmState.visibleChildren) { + if (view instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; + if (row.isHeadsUp() || row.isHeadsUpAnimatingAway()) { + continue; + } + } final float viewTop = view.getViewState().yTranslation; - if (viewTop >= shelfTop) { - view.getViewState().hidden = true; + view.getViewState().alpha = 0; } } } @@ -388,7 +394,8 @@ public class StackScrollAlgorithm { ambientState.getExpansionFraction(), true /* notification */); } - if (view.mustStayOnScreen() && viewState.yTranslation >= 0) { + if (ambientState.isShadeExpanded() && view.mustStayOnScreen() + && viewState.yTranslation >= 0) { // Even if we're not scrolled away we're in view and we're also not in the // shelf. We can relax the constraints and let us scroll off the top! float end = viewState.yTranslation + viewState.height + ambientState.getStackY(); 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 6d5c53609f81..6e201048abdb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -564,8 +564,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric); boolean deviceDreaming = mUpdateMonitor.isDreaming(); - boolean bypass = mKeyguardBypassController.getBypassEnabled(); - + boolean bypass = mKeyguardBypassController.getBypassEnabled() + || mKeyguardBypassController.getUserHasDeviceEntryIntent(); if (!mUpdateMonitor.isDeviceInteractive()) { if (!mKeyguardViewController.isShowing()) { return bypass ? MODE_WAKE_AND_UNLOCK : MODE_ONLY_WAKE; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 1361acb1e156..596fce5f7009 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -37,7 +37,6 @@ import android.view.ViewGroup; import android.view.ViewStub; import android.widget.LinearLayout; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -76,9 +75,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public static final int FADE_IN_DURATION = 320; public static final int FADE_IN_DELAY = 50; private PhoneStatusBarView mStatusBar; - private StatusBarStateController mStatusBarStateController; - private KeyguardStateController mKeyguardStateController; - private NetworkController mNetworkController; + private final StatusBarStateController mStatusBarStateController; + private final KeyguardStateController mKeyguardStateController; + private final NetworkController mNetworkController; private LinearLayout mSystemIconArea; private View mClockView; private View mOngoingCallChip; @@ -86,15 +85,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private View mCenteredIconArea; private int mDisabled1; private int mDisabled2; - private StatusBar mStatusBarComponent; + private final StatusBar mStatusBarComponent; private DarkIconManager mDarkIconManager; private View mOperatorNameFrame; - private CommandQueue mCommandQueue; - private OngoingCallController mOngoingCallController; + private final CommandQueue mCommandQueue; + private final OngoingCallController mOngoingCallController; private final SystemStatusAnimationScheduler mAnimationScheduler; private final StatusBarLocationPublisher mLocationPublisher; - private NotificationIconAreaController mNotificationIconAreaController; private final FeatureFlags mFeatureFlags; + private final NotificationIconAreaController mNotificationIconAreaController; + private final StatusBarIconController mStatusBarIconController; private List<String> mBlockedIcons = new ArrayList<>(); @@ -118,23 +118,25 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue SystemStatusAnimationScheduler animationScheduler, StatusBarLocationPublisher locationPublisher, NotificationIconAreaController notificationIconAreaController, - FeatureFlags featureFlags + FeatureFlags featureFlags, + StatusBarIconController statusBarIconController, + KeyguardStateController keyguardStateController, + NetworkController networkController, + StatusBarStateController statusBarStateController, + StatusBar statusBarComponent, + CommandQueue commandQueue ) { mOngoingCallController = ongoingCallController; mAnimationScheduler = animationScheduler; mLocationPublisher = locationPublisher; mNotificationIconAreaController = notificationIconAreaController; mFeatureFlags = featureFlags; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mKeyguardStateController = Dependency.get(KeyguardStateController.class); - mNetworkController = Dependency.get(NetworkController.class); - mStatusBarStateController = Dependency.get(StatusBarStateController.class); - mStatusBarComponent = Dependency.get(StatusBar.class); - mCommandQueue = Dependency.get(CommandQueue.class); + mStatusBarIconController = statusBarIconController; + mKeyguardStateController = keyguardStateController; + mNetworkController = networkController; + mStatusBarStateController = statusBarStateController; + mStatusBarComponent = statusBarComponent; + mCommandQueue = commandQueue; } @Override @@ -160,7 +162,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock)); mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength)); mDarkIconManager.setBlockList(mBlockedIcons); - Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager); + mStatusBarIconController.addIconGroup(mDarkIconManager); mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area); mClockView = mStatusBar.findViewById(R.id.clock); mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip); @@ -199,7 +201,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onDestroyView() { super.onDestroyView(); - Dependency.get(StatusBarIconController.class).removeIconGroup(mDarkIconManager); + mStatusBarIconController.removeIconGroup(mDarkIconManager); if (mNetworkController.hasEmergencyCryptKeeperText()) { mNetworkController.removeCallback(mSignalCallback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt index b148eeba2cf5..07618da4451a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt @@ -18,6 +18,7 @@ import android.content.Context import android.content.pm.ActivityInfo import android.content.res.Configuration import android.os.LocaleList +import android.view.View.LAYOUT_DIRECTION_RTL import com.android.systemui.statusbar.policy.ConfigurationController import java.util.ArrayList @@ -33,6 +34,7 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController { private var uiMode: Int = 0 private var localeList: LocaleList? = null private val context: Context + private var layoutDirection: Int init { val currentConfig = context.resources.configuration @@ -44,6 +46,7 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController { Configuration.UI_MODE_TYPE_CAR uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK localeList = currentConfig.locales + layoutDirection = currentConfig.layoutDirection } override fun notifyThemeChanged() { @@ -101,6 +104,13 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController { } } + if (layoutDirection != newConfig.layoutDirection) { + layoutDirection = newConfig.layoutDirection + listeners.filterForEach({ this.listeners.contains(it) }) { + it.onLayoutDirectionChanged(layoutDirection == LAYOUT_DIRECTION_RTL) + } + } + if (lastConfig.updateFrom(newConfig) and ActivityInfo.CONFIG_ASSETS_PATHS != 0) { listeners.filterForEach({ this.listeners.contains(it) }) { it.onOverlayChanged() @@ -116,6 +126,10 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController { override fun removeCallback(listener: ConfigurationController.ConfigurationListener) { listeners.remove(listener) } + + override fun isLayoutRtl(): Boolean { + return layoutDirection == LAYOUT_DIRECTION_RTL + } } // This could be done with a Collection.filter and Collection.forEach, but Collection.filter 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 2cb0a3a28901..ec669711e2db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -43,6 +43,7 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr @BypassOverride private val bypassOverride: Int private var hasFaceFeature: Boolean private var pendingUnlock: PendingUnlock? = null + var userHasDeviceEntryIntent: Boolean = false // ie: attempted udfps auth @IntDef( FACE_UNLOCK_BYPASS_NO_OVERRIDE, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java index 178974327a75..f1cde8a9be7a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java @@ -62,6 +62,14 @@ public class KeyguardIndicationTextView extends TextView { } /** + * Clears message queue. + */ + public void clearMessages() { + mMessages.clear(); + mKeyguardIndicationInfo.clear(); + } + + /** * Changes the text with an animation and makes sure a single indication is shown long enough. */ public void switchIndication(int textResId) { 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 fb3790cf4599..0fb26a2d0470 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -3646,8 +3646,6 @@ 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/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 31a432e2c451..c300b11b9a34 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -23,6 +23,7 @@ import static java.lang.Float.isNaN; import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Point; import android.graphics.Rect; import android.util.AttributeSet; import android.util.EventLog; @@ -52,6 +53,7 @@ public class PhoneStatusBarView extends PanelBar { private static final boolean DEBUG = StatusBar.DEBUG; private static final boolean DEBUG_GESTURES = false; private final CommandQueue mCommandQueue; + private final StatusBarContentInsetsProvider mContentInsetsProvider; StatusBar mBar; @@ -85,11 +87,10 @@ public class PhoneStatusBarView extends PanelBar { private int mCutoutSideNudge = 0; private boolean mHeadsUpVisible; - private int mRoundedCornerPadding = 0; - public PhoneStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); mCommandQueue = Dependency.get(CommandQueue.class); + mContentInsetsProvider = Dependency.get(StatusBarContentInsetsProvider.class); } public void setBar(StatusBar bar) { @@ -305,8 +306,6 @@ public class PhoneStatusBarView extends PanelBar { public void updateResources() { mCutoutSideNudge = getResources().getDimensionPixelSize( R.dimen.display_cutout_margin_consumption); - mRoundedCornerPadding = getResources().getDimensionPixelSize( - R.dimen.rounded_corner_content_padding); updateStatusBarHeight(); } @@ -341,8 +340,7 @@ public class PhoneStatusBarView extends PanelBar { private void updateLayoutForCutout() { updateStatusBarHeight(); updateCutoutLocation(StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay())); - updateSafeInsets(StatusBarWindowView.statusBarCornerCutoutMargins(mDisplayCutout, - getDisplay(), mRotationOrientation, mStatusBarHeight)); + updateSafeInsets(); } private void updateCutoutLocation(Pair<Integer, Integer> cornerCutoutMargins) { @@ -370,15 +368,18 @@ public class PhoneStatusBarView extends PanelBar { lp.height = bounds.height(); } - private void updateSafeInsets(Pair<Integer, Integer> cornerCutoutMargins) { - // Depending on our rotation, we may have to work around a cutout in the middle of the view, - // or letterboxing from the right or left sides. + private void updateSafeInsets() { + Rect contentRect = mContentInsetsProvider + .getStatusBarContentInsetsForRotation(RotationUtils.getExactRotation(getContext())); - Pair<Integer, Integer> padding = - StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner( - mDisplayCutout, cornerCutoutMargins, mRoundedCornerPadding); + Point size = new Point(); + getDisplay().getRealSize(size); - setPadding(padding.first, getPaddingTop(), padding.second, getPaddingBottom()); + setPadding( + contentRect.left, + getPaddingTop(), + size.x - contentRect.right, + getPaddingBottom()); } public void setHeadsUpVisible(boolean headsUpVisible) { 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 c68429a56225..173eec18ecc5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -55,6 +55,7 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.StatusBarManager; +import android.app.TaskInfo; import android.app.UiModeManager; import android.app.WallpaperInfo; import android.app.WallpaperManager; @@ -247,6 +248,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.SplashscreenContentDrawer; import com.android.wm.shell.startingsurface.StartingSurface; import java.io.FileDescriptor; @@ -269,7 +271,7 @@ public class StatusBar extends SystemUI implements DemoMode, ColorExtractor.OnColorsChangedListener, ConfigurationListener, StatusBarStateController.StateListener, LifecycleOwner, BatteryController.BatteryStateChangeCallback, - ActivityLaunchAnimator.KeyguardHandler { + ActivityLaunchAnimator.Callback { public static final boolean MULTIUSER_DEBUG = false; protected static final int MSG_HIDE_RECENT_APPS = 1020; @@ -439,6 +441,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final OngoingCallController mOngoingCallController; private final SystemStatusAnimationScheduler mAnimationScheduler; private final StatusBarLocationPublisher mStatusBarLocationPublisher; + private final StatusBarIconController mStatusBarIconController; // expanded notifications // the sliding/resizing panel within the notification window @@ -801,6 +804,7 @@ public class StatusBar extends SystemUI implements DemoMode, OngoingCallController ongoingCallController, SystemStatusAnimationScheduler animationScheduler, StatusBarLocationPublisher locationPublisher, + StatusBarIconController statusBarIconController, LockscreenShadeTransitionController lockscreenShadeTransitionController, FeatureFlags featureFlags, KeyguardUnlockAnimationController keyguardUnlockAnimationController, @@ -887,6 +891,7 @@ public class StatusBar extends SystemUI implements DemoMode, mOngoingCallController = ongoingCallController; mAnimationScheduler = animationScheduler; mStatusBarLocationPublisher = locationPublisher; + mStatusBarIconController = statusBarIconController; mFeatureFlags = featureFlags; mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; @@ -1188,7 +1193,14 @@ public class StatusBar extends SystemUI implements DemoMode, mAnimationScheduler, mStatusBarLocationPublisher, mNotificationIconAreaController, - mFeatureFlags), + mFeatureFlags, + mStatusBarIconController, + mKeyguardStateController, + mNetworkController, + mStatusBarStateController, + this, + mCommandQueue + ), CollapsedStatusBarFragment.TAG) .commit(); @@ -1421,9 +1433,7 @@ public class StatusBar extends SystemUI implements DemoMode, private void setUpPresenter() { // Set up the initial notification state. - mActivityLaunchAnimator = new ActivityLaunchAnimator(this, - mStartingSurfaceOptional.orElse(null), - mContext); + mActivityLaunchAnimator = new ActivityLaunchAnimator(this, mContext); mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( mNotificationShadeWindowViewController, mStackScrollerController.getNotificationListContainer(), @@ -2123,6 +2133,16 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled); } + @Override + public int getBackgroundColor(TaskInfo task) { + if (!mStartingSurfaceOptional.isPresent()) { + Log.w(TAG, "No starting surface, defaulting to SystemBGColor"); + return SplashscreenContentDrawer.getSystemBGColor(); + } + + return mStartingSurfaceOptional.get().getBackgroundColor(task); + } + public boolean isDeviceInVrMode() { return mPresenter.isDeviceInVrMode(); } @@ -2727,6 +2747,11 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController.dump(fd, pw, args); } + if (mLightRevealScrim != null) { + pw.println( + "mLightRevealScrim.getRevealAmount(): " + mLightRevealScrim.getRevealAmount()); + } + if (mStatusBarKeyguardViewManager != null) { mStatusBarKeyguardViewManager.dump(pw); } @@ -3925,7 +3950,11 @@ public class StatusBar extends SystemUI implements DemoMode, || !wakingUp && mWakefulnessLifecycle.getLastSleepReason() == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON) { mLightRevealScrim.setRevealEffect(mPowerButtonReveal); - } else if (!(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { + } else if (!wakingUp || !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { + // If we're going to sleep, but it's not from the power button, use the default reveal. + // If we're waking up, only use the default reveal if the biometric controller didn't + // already set it to the circular reveal because we're waking up from a fingerprint/face + // auth. mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE); } } @@ -4169,7 +4198,7 @@ public class StatusBar extends SystemUI implements DemoMode, private void vibrateForCameraGesture() { // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep. - mVibrator.vibrate(mCameraLaunchGestureVibrationEffect); + mVibrator.vibrate(mCameraLaunchGestureVibrationEffect, VIBRATION_ATTRIBUTES); } private static VibrationEffect getCameraGestureVibrationEffect(Vibrator vibrator, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt index edcf261115d2..fe1f63a34acd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone import android.content.Context import android.content.res.Resources import android.graphics.Rect +import android.util.Log import android.util.Pair import android.view.DisplayCutout import android.view.View.LAYOUT_DIRECTION_RTL @@ -155,13 +156,30 @@ class StatusBarContentInsetsProvider @Inject constructor( val dc = context.display.cutout val currentRotation = RotationUtils.getExactRotation(context) + val isRtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL + val roundedCornerPadding = rotatedResources + .getDimensionPixelSize(R.dimen.rounded_corner_content_padding) + val minDotWidth = rotatedResources + .getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding) + + val minLeft: Int + val minRight: Int + if (isRtl) { + minLeft = max(minDotWidth, roundedCornerPadding) + minRight = roundedCornerPadding + } else { + minLeft = roundedCornerPadding + minRight = max(minDotWidth, roundedCornerPadding) + } + return calculateInsetsForRotationWithRotatedResources( currentRotation, targetRotation, dc, windowManager.maximumWindowMetrics, rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height), - rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding)) + minLeft, + minRight) } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { @@ -212,9 +230,12 @@ fun getPrivacyChipBoundingRectForInsets( * Calculates the exact left and right positions for the status bar contents for the given * rotation * - * @param rot rotation for which to query the margins - * @param context systemui context - * @param rotatedResources resources constructed with the proper orientation set + * @param currentRotation current device rotation + * @param targetRotation rotation for which to calculate the status bar content rect + * @param displayCutout [DisplayCutout] for the curren display. possibly null + * @param windowMetrics [WindowMetrics] for the current window + * @param statusBarHeight height of the status bar for the target rotation + * @param roundedCornerPadding from rounded_corner_content_padding * * @see [RotationUtils#getResourcesForRotation] */ @@ -224,7 +245,8 @@ fun calculateInsetsForRotationWithRotatedResources( displayCutout: DisplayCutout?, windowMetrics: WindowMetrics, statusBarHeight: Int, - roundedCornerPadding: Int + minLeft: Int, + minRight: Int ): Rect { /* TODO: Check if this is ever used for devices with no rounded corners @@ -242,7 +264,8 @@ fun calculateInsetsForRotationWithRotatedResources( rotZeroBounds.bottom, currentBounds.width(), currentBounds.height(), - roundedCornerPadding, + minLeft, + minRight, targetRotation, currentRotation) @@ -256,7 +279,10 @@ fun calculateInsetsForRotationWithRotatedResources( * @param sbHeight appropriate status bar height for this rotation * @param width display width calculated for ROTATION_NONE * @param height display height calculated for ROTATION_NONE - * @param roundedCornerPadding rounded_corner_content_padding dimension + * @param cWidth display width in our current rotation + * @param cHeight display height in our current rotation + * @param minLeft the minimum padding to enforce on the left + * @param minRight the minimum padding to enforce on the right * @param targetRotation the rotation for which to calculate margins * @param currentRotation the rotation from which the display cutout was generated * @@ -270,7 +296,8 @@ private fun getStatusBarLeftRight( height: Int, cWidth: Int, cHeight: Int, - roundedCornerPadding: Int, + minLeft: Int, + minRight: Int, @Rotation targetRotation: Int, @Rotation currentRotation: Int ): Rect { @@ -279,9 +306,9 @@ private fun getStatusBarLeftRight( val cutoutRects = dc?.boundingRects if (cutoutRects == null || cutoutRects.isEmpty()) { - return Rect(roundedCornerPadding, + return Rect(minLeft, 0, - logicalDisplayWidth - roundedCornerPadding, + logicalDisplayWidth - minRight, sbHeight) } @@ -294,8 +321,8 @@ private fun getStatusBarLeftRight( // Size of the status bar window for the given rotation relative to our exact rotation val sbRect = sbRect(relativeRotation, sbHeight, Pair(cWidth, cHeight)) - var leftMargin = roundedCornerPadding - var rightMargin = roundedCornerPadding + var leftMargin = minLeft + var rightMargin = minRight for (cutoutRect in cutoutRects) { // There is at most one non-functional area per short edge of the device. So if the status // bar doesn't share a short edge with the cutout, we can ignore its insets because there @@ -306,11 +333,11 @@ private fun getStatusBarLeftRight( if (cutoutRect.touchesLeftEdge(relativeRotation, cWidth, cHeight)) { - val l = max(roundedCornerPadding, cutoutRect.logicalWidth(relativeRotation)) + val l = max(minLeft, cutoutRect.logicalWidth(relativeRotation)) leftMargin = max(l, leftMargin) } else if (cutoutRect.touchesRightEdge(relativeRotation, cWidth, cHeight)) { val logicalWidth = cutoutRect.logicalWidth(relativeRotation) - rightMargin = max(roundedCornerPadding, logicalWidth) + rightMargin = max(minRight, logicalWidth) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index aa58527cb32e..47deb1f0084b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -24,12 +24,14 @@ import android.app.KeyguardManager; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemClock; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.util.Log; import android.util.Slog; +import android.view.View; import android.view.accessibility.AccessibilityManager; import android.widget.TextView; @@ -394,8 +396,10 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } @Override - public void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded) { + public void onExpandClicked(NotificationEntry clickedEntry, View clickedView, + boolean nowExpanded) { mHeadsUpManager.setExpanded(clickedEntry, nowExpanded); + mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), clickedView, "NOTIFICATION_CLICK"); if (nowExpanded) { if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { mShadeTransitionController.goToLockedShade(clickedEntry.getRow()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 95712cd303f5..262dc837f22c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -132,7 +132,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, if (!row.isPinned()) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); } - mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */); + mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */); mPendingRemoteInputView = clicked; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index 64a497ddd317..329293409dc2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -245,8 +245,19 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { * @param slotName name of the icon slot to remove from the ignored list */ public void removeIgnoredSlot(String slotName) { - if (mIgnoredSlots.contains(slotName)) { - mIgnoredSlots.remove(slotName); + mIgnoredSlots.remove(slotName); + + requestLayout(); + } + + /** + * Remove a list of slots from the list of ignored icon slots. + * It will then be shown when set to visible by the {@link StatusBarIconController}. + * @param slots name of the icon slots to remove from the ignored list + */ + public void removeIgnoredSlots(List<String> slots) { + for (String slot : slots) { + mIgnoredSlots.remove(slot); } requestLayout(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt index 4167287a504e..a5b868b6f8a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt @@ -199,10 +199,9 @@ class UnlockedScreenOffAnimationController @Inject constructor( // We currently draw both the light reveal scrim, and the AOD UI, in the shade. If it's // already expanded and showing notifications/QS, the animation looks really messy. For now, - // disable it if the notification panel is expanded. + // disable it if the notification panel is not fully collapsed. if (!this::statusBar.isInitialized || - statusBar.notificationPanelViewController.isFullyExpanded || - statusBar.notificationPanelViewController.isExpanding) { + !statusBar.notificationPanelViewController.isFullyCollapsed) { return false } 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 716d1dbc6462..b6e8bd8bf7c1 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 @@ -89,6 +89,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarLocationPublisher; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; @@ -216,6 +217,7 @@ public interface StatusBarPhoneModule { OngoingCallController ongoingCallController, SystemStatusAnimationScheduler animationScheduler, StatusBarLocationPublisher locationPublisher, + StatusBarIconController statusBarIconController, LockscreenShadeTransitionController transitionController, FeatureFlags featureFlags, KeyguardUnlockAnimationController keyguardUnlockAnimationController, @@ -305,6 +307,7 @@ public interface StatusBarPhoneModule { ongoingCallController, animationScheduler, locationPublisher, + statusBarIconController, transitionController, featureFlags, keyguardUnlockAnimationController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java index c2bd87c6276f..3a05ec78a8b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java @@ -30,6 +30,9 @@ public interface ConfigurationController extends CallbackController<Configuratio /** Alert controller of a change in between light and dark themes. */ void notifyThemeChanged(); + /** Query the current configuration's layout direction */ + boolean isLayoutRtl(); + interface ConfigurationListener { default void onConfigChanged(Configuration newConfig) {} default void onDensityOrFontScaleChanged() {} @@ -38,5 +41,6 @@ public interface ConfigurationController extends CallbackController<Configuratio default void onUiModeChanged() {} default void onThemeChanged() {} default void onLocaleListChanged() {} + default void onLayoutDirectionChanged(boolean isLayoutRtl) {} } } 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 bbaf65a399a9..84d7c05ddc14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -272,6 +272,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene public void onEnd(@NonNull WindowInsetsAnimation animation) { super.onEnd(animation); if (animation.getTypeMask() == WindowInsets.Type.ime()) { + mEntry.mRemoteEditImeAnimatingAway = false; mEntry.mRemoteEditImeVisible = mEditText.getRootWindowInsets().isVisible(WindowInsets.Type.ime()); if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) { @@ -392,6 +393,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mSendButton.setVisibility(INVISIBLE); mProgressBar.setVisibility(VISIBLE); mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime(); + mEntry.mRemoteEditImeAnimatingAway = true; mController.addSpinning(mEntry.getKey(), mToken); mController.removeRemoteInput(mEntry, mToken); mEditText.mShowImeOnInputConnection = false; 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 4e921a036b36..c94eaeda3906 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -539,6 +539,13 @@ public class UserSwitcherController implements Dumpable { mSecondaryUser = userInfo.id; } unpauseRefreshUsers = true; + if (mGuestUserAutoCreated) { + // Guest user must be scheduled for creation AFTER switching to the target user. + // This avoids lock contention which will produce UX bugs on the keyguard + // (b/193933686). + // TODO(b/191067027): Move guest user recreation to system_server + guaranteeGuestPresent(); + } } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) { forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); @@ -670,10 +677,6 @@ public class UserSwitcherController implements Dumpable { switchToUserId(newGuestId); mUserManager.removeUser(currentUser.id); } else { - if (mGuestUserAutoCreated) { - // TODO(b/191067027): Move guest recreation to system_server - scheduleGuestCreation(); - } switchToUserId(targetUserId); mUserManager.removeUser(currentUser.id); } diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index 843630b35e17..c3b4fbe9a13d 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -197,8 +197,10 @@ public class ThemeOverlayApplier implements Dumpable { .collect(Collectors.toList()); OverlayManagerTransaction.Builder transaction = getTransactionBuilder(); + HashSet<OverlayIdentifier> identifiersPending = new HashSet<>(); if (pendingCreation != null) { for (FabricatedOverlay overlay : pendingCreation) { + identifiersPending.add(overlay.getIdentifier()); transaction.registerFabricatedOverlay(overlay); } } @@ -206,14 +208,14 @@ public class ThemeOverlayApplier implements Dumpable { for (Pair<String, String> packageToDisable : overlaysToDisable) { OverlayIdentifier overlayInfo = new OverlayIdentifier(packageToDisable.second); setEnabled(transaction, overlayInfo, packageToDisable.first, currentUser, - managedProfiles, false); + managedProfiles, false, identifiersPending.contains(overlayInfo)); } for (String category : THEME_CATEGORIES) { if (categoryToPackage.containsKey(category)) { OverlayIdentifier overlayInfo = categoryToPackage.get(category); setEnabled(transaction, overlayInfo, category, currentUser, managedProfiles, - true); + true, identifiersPending.contains(overlayInfo)); } } @@ -233,7 +235,7 @@ public class ThemeOverlayApplier implements Dumpable { @AnyThread private void setEnabled(OverlayManagerTransaction.Builder transaction, OverlayIdentifier identifier, String category, int currentUser, - Set<UserHandle> managedProfiles, boolean enabled) { + Set<UserHandle> managedProfiles, boolean enabled, boolean pendingCreation) { if (DEBUG) { Log.d(TAG, "setEnabled: " + identifier.getPackageName() + " category: " + category + ": " + enabled); @@ -241,7 +243,7 @@ public class ThemeOverlayApplier implements Dumpable { OverlayInfo overlayInfo = mOverlayManager.getOverlayInfo(identifier, UserHandle.of(currentUser)); - if (overlayInfo == null) { + if (overlayInfo == null && !pendingCreation) { Log.i(TAG, "Won't enable " + identifier + ", it doesn't exist for user" + currentUser); return; diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java index 0ecc4e25047f..4a4f2e9710dd 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java @@ -31,8 +31,10 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.util.time.SystemClock; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import javax.inject.Inject; @@ -52,9 +54,11 @@ public class QuickAccessWalletController { } private static final String TAG = "QAWController"; + private static final long RECREATION_TIME_WINDOW = TimeUnit.MINUTES.toMillis(10L); private final Context mContext; private final Executor mExecutor; private final SecureSettings mSecureSettings; + private final SystemClock mClock; private QuickAccessWalletClient mQuickAccessWalletClient; private ContentObserver mWalletPreferenceObserver; @@ -62,17 +66,21 @@ public class QuickAccessWalletController { private int mWalletPreferenceChangeEvents = 0; private int mDefaultPaymentAppChangeEvents = 0; private boolean mWalletEnabled = false; + private long mQawClientCreatedTimeMillis; @Inject public QuickAccessWalletController( Context context, @Main Executor executor, SecureSettings secureSettings, - QuickAccessWalletClient quickAccessWalletClient) { + QuickAccessWalletClient quickAccessWalletClient, + SystemClock clock) { mContext = context; mExecutor = executor; mSecureSettings = secureSettings; mQuickAccessWalletClient = quickAccessWalletClient; + mClock = clock; + mQawClientCreatedTimeMillis = mClock.elapsedRealtime(); } /** @@ -143,6 +151,11 @@ public class QuickAccessWalletController { */ public void queryWalletCards( QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) { + if (mClock.elapsedRealtime() - mQawClientCreatedTimeMillis + > RECREATION_TIME_WINDOW) { + Log.i(TAG, "Re-creating the QAW client to avoid stale."); + reCreateWalletClient(); + } if (!mQuickAccessWalletClient.isWalletFeatureAvailable()) { Log.d(TAG, "QuickAccessWallet feature is not available."); return; @@ -162,6 +175,7 @@ public class QuickAccessWalletController { */ public void reCreateWalletClient() { mQuickAccessWalletClient = QuickAccessWalletClient.create(mContext); + mQawClientCreatedTimeMillis = mClock.elapsedRealtime(); } private void setupDefaultPaymentAppObserver( |