Merge "Update Incremental TEST_MAPPING" into main
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 8ba8b8c..0699bc1 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -32,6 +32,7 @@
import android.util.AttributeSet;
import android.util.TimeUtils;
import android.util.Xml;
+import android.view.Choreographer;
import android.view.InflateException;
import org.xmlpull.v1.XmlPullParser;
@@ -153,7 +154,13 @@
*/
public static long getExpectedPresentationTimeNanos() {
AnimationState state = sAnimationState.get();
- return state.mExpectedPresentationTimeNanos;
+ if (state.animationClockLocked) {
+ return state.mExpectedPresentationTimeNanos;
+ }
+ // When this methoed is called outside of a Choreographer callback,
+ // we obtain the value of expectedPresentTimeNanos from the Choreographer.
+ // This helps avoid returning a time that could potentially be earlier than current time.
+ return Choreographer.getInstance().getLatestExpectedPresentTimeNanos();
}
/**
diff --git a/core/tests/coretests/src/android/content/TEST_MAPPING b/core/tests/coretests/src/android/content/TEST_MAPPING
new file mode 100644
index 0000000..bbc2458
--- /dev/null
+++ b/core/tests/coretests/src/android/content/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.content.ContentCaptureOptionsTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
new file mode 100644
index 0000000..f8beac2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.contentcapture"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
new file mode 100644
index 0000000..3cd4e17
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.contentprotection"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index af8ef17..7699b4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -737,12 +737,23 @@
Intent fillInIntent2 = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
+ final ActivityOptions activityOptions1 = options1 != null
+ ? ActivityOptions.fromBundle(options1) : ActivityOptions.makeBasic();
+ final ActivityOptions activityOptions2 = options2 != null
+ ? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
if (samePackage(packageName1, packageName2, userId1, userId2)) {
if (supportMultiInstancesSplit(packageName1)) {
fillInIntent1 = new Intent();
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
fillInIntent2 = new Intent();
fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+
+ if (shortcutInfo1 != null) {
+ activityOptions1.setApplyMultipleTaskFlagForShortcut(true);
+ }
+ if (shortcutInfo2 != null) {
+ activityOptions2.setApplyMultipleTaskFlagForShortcut(true);
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
pendingIntent2 = null;
@@ -754,9 +765,10 @@
Toast.LENGTH_SHORT).show();
}
}
- mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1, options1,
- pendingIntent2, fillInIntent2, shortcutInfo2, options2, splitPosition, splitRatio,
- remoteTransition, instanceId);
+ mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1,
+ activityOptions1.toBundle(), pendingIntent2, fillInIntent2, shortcutInfo2,
+ activityOptions2.toBundle(), splitPosition, splitRatio, remoteTransition,
+ instanceId);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index e52fd00..dc78c9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -407,7 +407,7 @@
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
}
// Rotation change of independent non display window container.
- if (change.getParent() == null
+ if (change.getParent() == null && !change.hasFlags(FLAG_IS_DISPLAY)
&& change.getStartRotation() != change.getEndRotation()) {
startRotationAnimation(startTransaction, change, info,
ROTATION_ANIMATION_ROTATE, animations, onAnimFinish);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index a242c72..c22cc6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -186,9 +186,12 @@
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
final RemoteTransition remoteTransition = mRequestedRemotes.get(mergeTarget);
- final IRemoteTransition remote = remoteTransition.getRemoteTransition();
+ if (remoteTransition == null) return;
+
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Merge into remote: %s",
remoteTransition);
+
+ final IRemoteTransition remote = remoteTransition.getRemoteTransition();
if (remote == null) return;
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index 6d6a9f8..bbe5e06 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -10,5 +10,8 @@
per-file *Image* = file:/graphics/java/android/graphics/OWNERS
+per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=set noparent
+per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
+
# Haptics team also works on Ringtone
per-file *Ringtone* = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
index 96894d1..e12d828 100644
--- a/media/jni/OWNERS
+++ b/media/jni/OWNERS
@@ -3,3 +3,6 @@
# extra for TV related files
per-file android_media_tv_*=hgchen@google.com,quxiangfang@google.com
+
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=set noparent
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 1d433e7..5bc27195 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -59,7 +59,7 @@
@Override
public boolean isAvailable() {
- return true;
+ return mWifiManager != null;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 0670ec3..79a1728 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -254,7 +254,7 @@
/** Migrate the indication area to the new keyguard root view. */
// TODO(b/280067944): Tracking bug.
@JvmField
- val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true)
+ val MIGRATE_INDICATION_AREA = releasedFlag(236, "migrate_indication_area")
/**
* Migrate the bottom area to the new keyguard root view.
@@ -294,6 +294,11 @@
@JvmField
val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl")
+ /** Migrate the status view from the notification panel to keyguard root view. */
+ // TODO(b/291767565): Tracking bug.
+ @JvmField
+ val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag(243, "migrate_keyguard_status_view")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
new file mode 100644
index 0000000..eaecda5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 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.flags
+
+import android.util.Log
+import com.android.systemui.Dependency
+
+/**
+ * This class promotes best practices for flag guarding System UI view refactors.
+ * * [isEnabled] allows changing an implementation.
+ * * [assertDisabled] allows authors to flag code as being "dead" when the flag gets enabled and
+ * ensure that it is not being invoked accidentally in the post-flag refactor.
+ * * [expectEnabled] allows authors to guard new code with a "safe" alternative when invoked on
+ * flag-disabled builds, but with a check that should crash eng builds or tests when the
+ * expectation is violated.
+ *
+ * The constructors prefer that you provide a [FeatureFlags] instance, but does not require it,
+ * falling back to [Dependency.get]. This fallback should ONLY be used to flag-guard code changes
+ * inside views where injecting flag values after initialization can be error-prone.
+ */
+class ViewRefactorFlag
+private constructor(
+ private val injectedFlags: FeatureFlags?,
+ private val flag: BooleanFlag,
+ private val readFlagValue: (FeatureFlags) -> Boolean
+) {
+ @JvmOverloads
+ constructor(
+ flags: FeatureFlags? = null,
+ flag: UnreleasedFlag
+ ) : this(flags, flag, { it.isEnabled(flag) })
+
+ @JvmOverloads
+ constructor(
+ flags: FeatureFlags? = null,
+ flag: ReleasedFlag
+ ) : this(flags, flag, { it.isEnabled(flag) })
+
+ /** Whether the flag is enabled. Called to switch between an old behavior and a new behavior. */
+ val isEnabled by lazy {
+ @Suppress("DEPRECATION")
+ val featureFlags = injectedFlags ?: Dependency.get(FeatureFlags::class.java)
+ readFlagValue(featureFlags)
+ }
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ *
+ * Example usage:
+ * ```
+ * public void setController(NotificationShelfController notificationShelfController) {
+ * mShelfRefactor.assertDisabled();
+ * mController = notificationShelfController;
+ * }
+ * ````
+ */
+ fun assertDisabled() = check(!isEnabled) { "Code path not supported when $flag is enabled." }
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ *
+ * Example usage:
+ * ```
+ * public void setShelfIcons(NotificationIconContainer icons) {
+ * if (mShelfRefactor.expectEnabled()) {
+ * mShelfIcons = icons;
+ * }
+ * }
+ * ```
+ */
+ fun expectEnabled(): Boolean {
+ if (!isEnabled) {
+ val message = "Code path not supported when $flag is disabled."
+ Log.wtf(TAG, message, Exception(message))
+ }
+ return isEnabled
+ }
+
+ private companion object {
+ private const val TAG = "ViewRefactorFlag"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ddd4a2b..e0834bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2982,6 +2982,7 @@
}
private void onKeyguardExitFinished() {
+ if (DEBUG) Log.d(TAG, "onKeyguardExitFinished()");
// only play "unlock" noises if not on a call (since the incall UI
// disables the keyguard)
if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
@@ -3203,13 +3204,14 @@
flags |= StatusBarManager.DISABLE_RECENT;
}
- if (mPowerGestureIntercepted) {
+ if (mPowerGestureIntercepted && mOccluded && isSecure()) {
flags |= StatusBarManager.DISABLE_RECENT;
}
if (DEBUG) {
Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded
+ " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons
+ + " mPowerGestureIntercepted=" + mPowerGestureIntercepted
+ " --> flags=0x" + Integer.toHexString(flags));
}
@@ -3437,6 +3439,7 @@
pw.print(" mPendingLock: "); pw.println(mPendingLock);
pw.print(" wakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
pw.print(" mPendingPinLock: "); pw.println(mPendingPinLock);
+ pw.print(" mPowerGestureIntercepted: "); pw.println(mPowerGestureIntercepted);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
index 4ec5f46..7a989cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
@@ -19,7 +19,6 @@
import android.view.View;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -52,7 +51,6 @@
mActivatableNotificationViewController = activatableNotificationViewController;
mKeyguardBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
- mView.setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 25a1dc6..3f37c60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -24,7 +24,6 @@
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
-import android.util.Log;
import android.util.MathUtils;
import android.view.View;
import android.view.ViewGroup;
@@ -40,6 +39,8 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -95,8 +96,10 @@
private float mCornerAnimationDistance;
private NotificationShelfController mController;
private float mActualWidth = -1;
- private boolean mSensitiveRevealAnimEnabled;
- private boolean mShelfRefactorFlagEnabled;
+ private final ViewRefactorFlag mSensitiveRevealAnim =
+ new ViewRefactorFlag(Flags.SENSITIVE_REVEAL_ANIM);
+ private final ViewRefactorFlag mShelfRefactor =
+ new ViewRefactorFlag(Flags.NOTIFICATION_SHELF_REFACTOR);
private boolean mCanModifyColorOfNotifications;
private boolean mCanInteract;
private NotificationStackScrollLayout mHostLayout;
@@ -130,7 +133,7 @@
public void bind(AmbientState ambientState,
NotificationStackScrollLayoutController hostLayoutController) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mAmbientState = ambientState;
mHostLayoutController = hostLayoutController;
hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> {
@@ -140,7 +143,7 @@
public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout,
NotificationRoundnessManager roundnessManager) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mAmbientState = ambientState;
mHostLayout = hostLayout;
mRoundnessManager = roundnessManager;
@@ -268,7 +271,7 @@
}
final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
- if (mSensitiveRevealAnimEnabled && viewState.hidden) {
+ if (mSensitiveRevealAnim.isEnabled() && viewState.hidden) {
// if the shelf is hidden, position it at the end of the stack (plus the clip
// padding), such that when it appears animated, it will smoothly move in from the
// bottom, without jump cutting any notifications
@@ -279,7 +282,7 @@
}
private int getSpeedBumpIndex() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getSpeedBumpIndex();
} else {
return mHostLayoutController.getSpeedBumpIndex();
@@ -413,7 +416,7 @@
expandingAnimated, isLastChild, shelfClipStart);
// TODO(b/172289889) scale mPaddingBetweenElements with expansion amount
- if ((!mSensitiveRevealAnimEnabled && ((isLastChild && !child.isInShelf())
+ if ((!mSensitiveRevealAnim.isEnabled() && ((isLastChild && !child.isInShelf())
|| backgroundForceHidden)) || aboveShelf) {
notificationClipEnd = shelfStart + getIntrinsicHeight();
} else {
@@ -462,7 +465,7 @@
// if the shelf is visible, but if the shelf is hidden, it causes incorrect curling.
// notificationClipEnd handles the discrepancy between a visible and hidden shelf,
// so we use that when on the keyguard (and while animating away) to reduce curling.
- final float keyguardSafeShelfStart = !mSensitiveRevealAnimEnabled
+ final float keyguardSafeShelfStart = !mSensitiveRevealAnim.isEnabled()
&& mAmbientState.isOnKeyguard() ? notificationClipEnd : shelfStart;
updateCornerRoundnessOnScroll(anv, viewStart, keyguardSafeShelfStart);
}
@@ -504,7 +507,7 @@
}
private ExpandableView getHostLayoutChildAt(int index) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return (ExpandableView) mHostLayout.getChildAt(index);
} else {
return mHostLayoutController.getChildAt(index);
@@ -512,7 +515,7 @@
}
private int getHostLayoutChildCount() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getChildCount();
} else {
return mHostLayoutController.getChildCount();
@@ -520,7 +523,7 @@
}
private boolean canModifyColorOfNotifications() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mCanModifyColorOfNotifications && mAmbientState.isShadeExpanded();
} else {
return mController.canModifyColorOfNotifications();
@@ -583,7 +586,7 @@
}
private boolean isViewAffectedBySwipe(ExpandableView expandableView) {
- if (!mShelfRefactorFlagEnabled) {
+ if (!mShelfRefactor.isEnabled()) {
return mHostLayoutController.isViewAffectedBySwipe(expandableView);
} else {
return mRoundnessManager.isViewAffectedBySwipe(expandableView);
@@ -607,7 +610,7 @@
}
private View getHostLayoutTransientView(int index) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getTransientView(index);
} else {
return mHostLayoutController.getTransientView(index);
@@ -615,7 +618,7 @@
}
private int getHostLayoutTransientViewCount() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getTransientViewCount();
} else {
return mHostLayoutController.getTransientViewCount();
@@ -961,7 +964,7 @@
@Override
public void onStateChanged(int newState) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mStatusBarState = newState;
updateInteractiveness();
}
@@ -975,7 +978,7 @@
}
private boolean canInteract() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mCanInteract;
} else {
return mStatusBarState == StatusBarState.KEYGUARD;
@@ -1018,32 +1021,18 @@
return false;
}
- private void assertRefactorFlagDisabled() {
- if (mShelfRefactorFlagEnabled) {
- NotificationShelfController.throwIllegalFlagStateError(false);
- }
- }
-
- private boolean checkRefactorFlagEnabled() {
- if (!mShelfRefactorFlagEnabled) {
- Log.wtf(TAG,
- "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is disabled.");
- }
- return mShelfRefactorFlagEnabled;
- }
-
public void setController(NotificationShelfController notificationShelfController) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mController = notificationShelfController;
}
public void setCanModifyColorOfNotifications(boolean canModifyColorOfNotifications) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mCanModifyColorOfNotifications = canModifyColorOfNotifications;
}
public void setCanInteract(boolean canInteract) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mCanInteract = canInteract;
updateInteractiveness();
}
@@ -1053,27 +1042,15 @@
}
private int getIndexOfViewInHostLayout(ExpandableView child) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.indexOfChild(child);
} else {
return mHostLayoutController.indexOfChild(child);
}
}
- /**
- * Set whether the sensitive reveal animation feature flag is enabled
- * @param enabled true if enabled
- */
- public void setSensitiveRevealAnimEnabled(boolean enabled) {
- mSensitiveRevealAnimEnabled = enabled;
- }
-
- public void setRefactorFlagEnabled(boolean enabled) {
- mShelfRefactorFlagEnabled = enabled;
- }
-
public void requestRoundnessResetFor(ExpandableView child) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
child.requestRoundnessReset(SHELF_SCROLL);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
index 1619dda..8a3e217 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
@@ -16,11 +16,8 @@
package com.android.systemui.statusbar
-import android.util.Log
import android.view.View
import android.view.View.OnClickListener
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -49,29 +46,4 @@
/** @see View.setOnClickListener */
fun setOnClickListener(listener: OnClickListener)
-
- companion object {
- @JvmStatic
- fun assertRefactorFlagDisabled(featureFlags: FeatureFlags) {
- if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
- throwIllegalFlagStateError(expected = false)
- }
- }
-
- @JvmStatic
- fun checkRefactorFlagEnabled(featureFlags: FeatureFlags): Boolean =
- featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR).also { enabled ->
- if (!enabled) {
- Log.wtf("NotifShelf", getErrorMessage(expected = true))
- }
- }
-
- @JvmStatic
- fun throwIllegalFlagStateError(expected: Boolean): Nothing =
- error(getErrorMessage(expected))
-
- private fun getErrorMessage(expected: Boolean): String =
- "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is " +
- if (expected) "disabled" else "enabled"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 1cf9c1e..1c5aa3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -3,10 +3,10 @@
import android.util.FloatProperty
import android.view.View
import androidx.annotation.FloatRange
-import com.android.systemui.Dependency
import com.android.systemui.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.ViewRefactorFlag
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import kotlin.math.abs
@@ -46,14 +46,14 @@
@JvmDefault
val topCornerRadius: Float
get() =
- if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.topCornerRadius
+ if (roundableState.newHeadsUpAnim.isEnabled) roundableState.topCornerRadius
else topRoundness * maxRadius
/** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */
@JvmDefault
val bottomCornerRadius: Float
get() =
- if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.bottomCornerRadius
+ if (roundableState.newHeadsUpAnim.isEnabled) roundableState.bottomCornerRadius
else bottomRoundness * maxRadius
/** Get and update the current radii */
@@ -335,13 +335,12 @@
internal val targetView: View,
private val roundable: Roundable,
maxRadius: Float,
- private val featureFlags: FeatureFlags = Dependency.get(FeatureFlags::class.java)
+ featureFlags: FeatureFlags? = null
) {
internal var maxRadius = maxRadius
private set
- internal val newHeadsUpAnimFlagEnabled
- get() = featureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS)
+ internal val newHeadsUpAnim = ViewRefactorFlag(featureFlags, Flags.IMPROVED_HUN_ANIMATIONS)
/** Animatable for top roundness */
private val topAnimatable = topAnimatable(roundable)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 1896080..9ecf50e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.collection.inflation;
-import static com.android.systemui.flags.Flags.NOTIFICATION_INLINE_REPLY_ANIMATION;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -176,8 +175,6 @@
entry.setRow(row);
mNotifBindPipeline.manageRow(entry, row);
mPresenter.onBindRow(row);
- row.setInlineReplyAnimationFlagEnabled(
- mFeatureFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 908c11a..36a8e98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -566,7 +566,7 @@
@Override
public float getTopCornerRadius() {
- if (isNewHeadsUpAnimFlagEnabled()) {
+ if (mImprovedHunAnimation.isEnabled()) {
return super.getTopCornerRadius();
}
@@ -576,7 +576,7 @@
@Override
public float getBottomCornerRadius() {
- if (isNewHeadsUpAnimFlagEnabled()) {
+ if (mImprovedHunAnimation.isEnabled()) {
return super.getBottomCornerRadius();
}
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 b34c281..42b99a1 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
@@ -77,6 +77,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -275,7 +276,8 @@
private OnExpandClickListener mOnExpandClickListener;
private View.OnClickListener mOnFeedbackClickListener;
private Path mExpandingClipPath;
- private boolean mIsInlineReplyAnimationFlagEnabled = false;
+ private final ViewRefactorFlag mInlineReplyAnimation =
+ new ViewRefactorFlag(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
// Listener will be called when receiving a long click event.
// Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -3121,10 +3123,6 @@
return showingLayout != null && showingLayout.requireRowToHaveOverlappingRendering();
}
- public void setInlineReplyAnimationFlagEnabled(boolean isEnabled) {
- mIsInlineReplyAnimationFlagEnabled = isEnabled;
- }
-
@Override
public void setActualHeight(int height, boolean notifyListeners) {
boolean changed = height != getActualHeight();
@@ -3144,7 +3142,7 @@
}
int contentHeight = Math.max(getMinHeight(), height);
for (NotificationContentView l : mLayouts) {
- if (mIsInlineReplyAnimationFlagEnabled) {
+ if (mInlineReplyAnimation.isEnabled()) {
l.setContentHeight(height);
} else {
l.setContentHeight(contentHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 7f23c1b..c8f13a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -28,10 +28,9 @@
import android.view.View;
import android.view.ViewOutlineProvider;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.statusbar.notification.RoundableState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.util.DumpUtilsKt;
@@ -50,7 +49,8 @@
private float mOutlineAlpha = -1f;
private boolean mAlwaysRoundBothCorners;
private Path mTmpPath = new Path();
- private final FeatureFlags mFeatureFlags;
+ protected final ViewRefactorFlag mImprovedHunAnimation =
+ new ViewRefactorFlag(Flags.IMPROVED_HUN_ANIMATIONS);
/**
* {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
@@ -126,7 +126,7 @@
return EMPTY_PATH;
}
float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius();
- if (!isNewHeadsUpAnimFlagEnabled() && (topRadius + bottomRadius > height)) {
+ if (!mImprovedHunAnimation.isEnabled() && (topRadius + bottomRadius > height)) {
float overShoot = topRadius + bottomRadius - height;
float currentTopRoundness = getTopRoundness();
float currentBottomRoundness = getBottomRoundness();
@@ -167,7 +167,6 @@
super(context, attrs);
setOutlineProvider(mProvider);
initDimens();
- mFeatureFlags = Dependency.get(FeatureFlags.class);
}
@Override
@@ -376,8 +375,4 @@
});
}
- // TODO(b/290365128) replace with ViewRefactorFlag
- protected boolean isNewHeadsUpAnimFlagEnabled() {
- return mFeatureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 23a58d2..22a87a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -21,7 +21,6 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl
@@ -66,7 +65,7 @@
override fun setOnClickListener(listener: View.OnClickListener) = unsupported
private val unsupported: Nothing
- get() = NotificationShelfController.throwIllegalFlagStateError(expected = true)
+ get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled")
}
/** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */
@@ -80,8 +79,6 @@
) {
ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
shelf.apply {
- setRefactorFlagEnabled(true)
- setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM))
// TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind()
notificationIconAreaController.setShelfIcons(shelfIcons)
repeatWhenAttached {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index b0f3f59..95e74f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -29,7 +29,6 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
@@ -57,7 +56,6 @@
private final SectionProvider mSectionProvider;
private final BypassController mBypassController;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
- private final FeatureFlags mFeatureFlags;
/**
* Used to read bouncer states.
*/
@@ -261,13 +259,12 @@
@NonNull SectionProvider sectionProvider,
@NonNull BypassController bypassController,
@Nullable StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator,
- @NonNull FeatureFlags featureFlags) {
+ @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator
+ ) {
mSectionProvider = sectionProvider;
mBypassController = bypassController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
- mFeatureFlags = featureFlags;
reload(context);
dumpManager.registerDumpable(this);
}
@@ -753,10 +750,6 @@
return mLargeScreenShadeInterpolator;
}
- public FeatureFlags getFeatureFlags() {
- return mFeatureFlags;
- }
-
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("mTopPadding=" + mTopPadding);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index c1ceb3c..d71bc2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -89,6 +89,7 @@
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.shade.ShadeController;
@@ -198,7 +199,8 @@
private Set<Integer> mDebugTextUsedYPositions;
private final boolean mDebugRemoveAnimation;
private final boolean mSensitiveRevealAnimEndabled;
- private boolean mAnimatedInsets;
+ private final ViewRefactorFlag mAnimatedInsets;
+ private final ViewRefactorFlag mShelfRefactor;
private int mContentHeight;
private float mIntrinsicContentHeight;
@@ -621,7 +623,9 @@
mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
- setAnimatedInsetsEnabled(featureFlags.isEnabled(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS));
+ mAnimatedInsets =
+ new ViewRefactorFlag(featureFlags, Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class);
@@ -660,7 +664,7 @@
mGroupMembershipManager = Dependency.get(GroupMembershipManager.class);
mGroupExpansionManager = Dependency.get(GroupExpansionManager.class);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- if (mAnimatedInsets) {
+ if (mAnimatedInsets.isEnabled()) {
setWindowInsetsAnimationCallback(mInsetsCallback);
}
}
@@ -730,11 +734,6 @@
}
@VisibleForTesting
- void setAnimatedInsetsEnabled(boolean enabled) {
- mAnimatedInsets = enabled;
- }
-
- @VisibleForTesting
public void updateFooter() {
if (mFooterView == null) {
return;
@@ -1773,7 +1772,7 @@
return;
}
mForcedScroll = v;
- if (mAnimatedInsets) {
+ if (mAnimatedInsets.isEnabled()) {
updateForcedScroll();
} else {
scrollTo(v);
@@ -1822,7 +1821,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- if (!mAnimatedInsets) {
+ if (!mAnimatedInsets.isEnabled()) {
mBottomInset = insets.getInsets(WindowInsets.Type.ime()).bottom;
}
mWaterfallTopInset = 0;
@@ -1830,11 +1829,11 @@
if (cutout != null) {
mWaterfallTopInset = cutout.getWaterfallInsets().top;
}
- if (mAnimatedInsets && !mIsInsetAnimationRunning) {
+ if (mAnimatedInsets.isEnabled() && !mIsInsetAnimationRunning) {
// update bottom inset e.g. after rotation
updateBottomInset(insets);
}
- if (!mAnimatedInsets) {
+ if (!mAnimatedInsets.isEnabled()) {
int range = getScrollRange();
if (mOwnScrollY > range) {
// HACK: We're repeatedly getting staggered insets here while the IME is
@@ -2714,7 +2713,7 @@
* @param listener callback for notification removed
*/
public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) {
- NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
+ mShelfRefactor.assertDisabled();
mOnNotificationRemovedListener = listener;
}
@@ -2727,7 +2726,7 @@
if (!mChildTransferInProgress) {
onViewRemovedInternal(expandableView, this);
}
- if (mAmbientState.getFeatureFlags().isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ if (mShelfRefactor.isEnabled()) {
mShelf.requestRoundnessResetFor(expandableView);
} else {
if (mOnNotificationRemovedListener != null) {
@@ -4943,18 +4942,12 @@
@Nullable
public ExpandableView getShelf() {
- if (NotificationShelfController.checkRefactorFlagEnabled(mAmbientState.getFeatureFlags())) {
- return mShelf;
- } else {
- return null;
- }
+ if (!mShelfRefactor.expectEnabled()) return null;
+ return mShelf;
}
public void setShelf(NotificationShelf shelf) {
- if (!NotificationShelfController.checkRefactorFlagEnabled(
- mAmbientState.getFeatureFlags())) {
- return;
- }
+ if (!mShelfRefactor.expectEnabled()) return;
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
@@ -4968,7 +4961,7 @@
}
public void setShelfController(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
+ mShelfRefactor.assertDisabled();
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index ef7375a..4668aa4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -65,6 +65,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.shared.model.KeyguardState;
@@ -205,6 +206,7 @@
private boolean mIsInTransitionToAod = false;
private final FeatureFlags mFeatureFlags;
+ private final ViewRefactorFlag mShelfRefactor;
private final NotificationTargetsHelper mNotificationTargetsHelper;
private final SecureSettings mSecureSettings;
private final NotificationDismissibilityProvider mDismissibilityProvider;
@@ -718,6 +720,7 @@
mShadeController = shadeController;
mNotifIconAreaController = notifIconAreaController;
mFeatureFlags = featureFlags;
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mNotificationTargetsHelper = notificationTargetsHelper;
mSecureSettings = secureSettings;
mDismissibilityProvider = dismissibilityProvider;
@@ -1432,7 +1435,7 @@
}
public void setShelfController(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
+ mShelfRefactor.assertDisabled();
mView.setShelfController(notificationShelfController);
}
@@ -1645,12 +1648,12 @@
}
public void setShelf(NotificationShelf shelf) {
- if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mView.setShelf(shelf);
}
public int getShelfHeight() {
- if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+ if (!mShelfRefactor.expectEnabled()) {
return 0;
}
ExpandableView shelf = mView.getShelf();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index e18c9d8..0bf0f4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -24,6 +24,8 @@
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -88,7 +90,7 @@
private final ArrayList<Rect> mTintAreas = new ArrayList<>();
private final Context mContext;
- private final FeatureFlags mFeatureFlags;
+ private final ViewRefactorFlag mShelfRefactor;
private int mAodIconAppearTranslation;
@@ -120,12 +122,13 @@
Optional<Bubbles> bubblesOptional,
DemoModeController demoModeController,
DarkIconDispatcher darkIconDispatcher,
- FeatureFlags featureFlags, StatusBarWindowController statusBarWindowController,
+ FeatureFlags featureFlags,
+ StatusBarWindowController statusBarWindowController,
ScreenOffAnimationController screenOffAnimationController) {
mContrastColorUtil = ContrastColorUtil.getInstance(context);
mContext = context;
mStatusBarStateController = statusBarStateController;
- mFeatureFlags = featureFlags;
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mStatusBarStateController.addCallback(this);
mMediaManager = notificationMediaManager;
mDozeParameters = dozeParameters;
@@ -179,12 +182,12 @@
}
public void setupShelf(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
+ mShelfRefactor.assertDisabled();
mShelfIcons = notificationShelfController.getShelfIcons();
}
public void setShelfIcons(NotificationIconContainer icons) {
- if (NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+ if (mShelfRefactor.expectEnabled()) {
mShelfIcons = icons;
}
}
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 a9135ac..ad8530d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -79,7 +79,6 @@
private final HeadsUpManagerPhone mHeadsUpManager;
private final AboveShelfObserver mAboveShelfObserver;
private final DozeScrimController mDozeScrimController;
- private final CentralSurfaces mCentralSurfaces;
private final NotificationsInteractor mNotificationsInteractor;
private final NotificationStackScrollLayoutController mNsslController;
private final LockscreenShadeTransitionController mShadeTransitionController;
@@ -107,7 +106,6 @@
NotificationShadeWindowController notificationShadeWindowController,
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
- CentralSurfaces centralSurfaces,
NotificationsInteractor notificationsInteractor,
LockscreenShadeTransitionController shadeTransitionController,
PowerInteractor powerInteractor,
@@ -128,8 +126,6 @@
mQsController = quickSettingsController;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
- // TODO: use KeyguardStateController#isOccluded to remove this dependency
- mCentralSurfaces = centralSurfaces;
mNotificationsInteractor = notificationsInteractor;
mNsslController = stackScrollerController;
mShadeTransitionController = shadeTransitionController;
@@ -283,7 +279,7 @@
@Override
public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
- if (mCentralSurfaces.isOccluded()) {
+ if (mKeyguardStateController.isOccluded()) {
boolean devicePublic = mLockscreenUserManager
.isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 5d09e06..a501e87 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -20,6 +20,11 @@
import com.android.systemui.Dependency;
+/**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ */
+@Deprecated
public abstract class TunerService {
public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 8cfe2ea..ccc0a79 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -56,7 +56,11 @@
/**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ * This class will interact with SecureSettings using the main looper.
*/
+@Deprecated
@SysUISingleton
public class TunerServiceImpl extends TunerService {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index 6decb88..5867a40c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -30,6 +30,8 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -43,12 +45,14 @@
@RunWithLooper
public class ExpandHelperTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private ExpandableNotificationRow mRow;
private ExpandHelper mExpandHelper;
private ExpandHelper.Callback mCallback;
@Before
public void setUp() throws Exception {
+ mFeatureFlags.setDefault(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mDependency.injectMockDependency(NotificationMediaManager.class);
allowTestableLooperAsMainThread();
@@ -56,7 +60,8 @@
NotificationTestHelper helper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mRow = helper.createRow();
mCallback = mock(ExpandHelper.Callback.class);
mExpandHelper = new ExpandHelper(context, mCallback, 10, 100);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 608778e..1dc8453 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -87,6 +88,7 @@
@RunWithLooper
public class ExpandableNotificationRowTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private NotificationTestHelper mNotificationTestHelper;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@@ -96,12 +98,10 @@
mNotificationTestHelper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL);
-
- FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
- fakeFeatureFlags.set(Flags.SENSITIVE_REVEAL_ANIM, false);
- mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags);
+ mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
}
@Test
@@ -183,6 +183,14 @@
}
@Test
+ public void testSetSensitiveOnNotifRowNotifiesOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnNotifRowNotifiesOfHeightChange();
+ }
+
+ @Test
public void testSetSensitiveOnNotifRowNotifiesOfHeightChange() throws Exception {
// GIVEN a sensitive notification row that's currently redacted
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
@@ -199,10 +207,19 @@
// WHEN the row is set to no longer be sensitive
row.setSensitive(false, true);
+ boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
// VERIFY that the height change listener is invoked
assertThat(row.getShowingLayout()).isSameInstanceAs(row.getPrivateLayout());
assertThat(row.getIntrinsicHeight()).isGreaterThan(0);
- verify(listener).onHeightChanged(eq(row), eq(false));
+ verify(listener).onHeightChanged(eq(row), eq(expectAnimation));
+ }
+
+ @Test
+ public void testSetSensitiveOnGroupRowNotifiesOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnGroupRowNotifiesOfHeightChange();
}
@Test
@@ -222,10 +239,19 @@
// WHEN the row is set to no longer be sensitive
group.setSensitive(false, true);
+ boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
// VERIFY that the height change listener is invoked
assertThat(group.getShowingLayout()).isSameInstanceAs(group.getPrivateLayout());
assertThat(group.getIntrinsicHeight()).isGreaterThan(0);
- verify(listener).onHeightChanged(eq(group), eq(false));
+ verify(listener).onHeightChanged(eq(group), eq(expectAnimation));
+ }
+
+ @Test
+ public void testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange();
}
@Test
@@ -254,7 +280,7 @@
assertThat(publicRow.getIntrinsicHeight()).isGreaterThan(0);
assertThat(publicRow.getPrivateLayout().getMinHeight())
.isEqualTo(publicRow.getPublicLayout().getMinHeight());
- verify(listener, never()).onHeightChanged(eq(publicRow), eq(false));
+ verify(listener, never()).onHeightChanged(eq(publicRow), anyBoolean());
}
private void measureAndLayout(ExpandableNotificationRow row) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 1a644d3..d21029d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -48,12 +48,16 @@
import android.view.LayoutInflater;
import android.widget.RemoteViews;
+import androidx.annotation.NonNull;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.TestableDependency;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -90,6 +94,7 @@
import org.mockito.ArgumentCaptor;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -130,14 +135,24 @@
private final NotificationDismissibilityProvider mDismissibilityProvider;
public final Runnable mFutureDismissalRunnable;
private @InflationFlag int mDefaultInflationFlags;
- private FeatureFlags mFeatureFlags;
+ private final FakeFeatureFlags mFeatureFlags;
public NotificationTestHelper(
Context context,
TestableDependency dependency,
TestableLooper testLooper) {
+ this(context, dependency, testLooper, new FakeFeatureFlags());
+ }
+
+ public NotificationTestHelper(
+ Context context,
+ TestableDependency dependency,
+ TestableLooper testLooper,
+ @NonNull FakeFeatureFlags featureFlags) {
mContext = context;
mTestLooper = testLooper;
+ mFeatureFlags = Objects.requireNonNull(featureFlags);
+ dependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
dependency.injectMockDependency(NotificationMediaManager.class);
dependency.injectMockDependency(NotificationShadeWindowController.class);
dependency.injectMockDependency(MediaOutputDialogFactory.class);
@@ -183,17 +198,12 @@
mFutureDismissalRunnable = mock(Runnable.class);
when(mOnUserInteractionCallback.registerFutureDismissal(any(), anyInt()))
.thenReturn(mFutureDismissalRunnable);
- mFeatureFlags = mock(FeatureFlags.class);
}
public void setDefaultInflationFlags(@InflationFlag int defaultInflationFlags) {
mDefaultInflationFlags = defaultInflationFlags;
}
- public void setFeatureFlags(FeatureFlags featureFlags) {
- mFeatureFlags = featureFlags;
- }
-
public ExpandableNotificationRowLogger getMockLogger() {
return mMockLogger;
}
@@ -527,6 +537,10 @@
@InflationFlag int extraInflationFlags,
int importance)
throws Exception {
+ // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
+ // set, but we do not want to override an existing value that is needed by a specific test.
+ mFeatureFlags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS);
+
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
mContext.LAYOUT_INFLATER_SERVICE);
mRow = (ExpandableNotificationRow) inflater.inflate(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
index 09382ec..3d75288 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
@@ -20,7 +20,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -42,7 +41,6 @@
private val bypassController = StackScrollAlgorithm.BypassController { false }
private val statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private val largeScreenShadeInterpolator = mock<LargeScreenShadeInterpolator>()
- private val featureFlags = mock<FeatureFlags>()
private lateinit var sut: AmbientState
@@ -55,8 +53,7 @@
sectionProvider,
bypassController,
statusBarKeyguardViewManager,
- largeScreenShadeInterpolator,
- featureFlags
+ largeScreenShadeInterpolator
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index f38881c..4b145d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -30,7 +30,6 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
@@ -65,7 +64,6 @@
@Mock private SectionHeaderController mPeopleHeaderController;
@Mock private SectionHeaderController mAlertingHeaderController;
@Mock private SectionHeaderController mSilentHeaderController;
- @Mock private FeatureFlags mFeatureFlag;
private NotificationSectionsManager mSectionsManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 8d751e3..1dc0ab0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -9,7 +9,9 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
@@ -20,6 +22,7 @@
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,22 +37,32 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
-class NotificationShelfTest : SysuiTestCase() {
+open class NotificationShelfTest : SysuiTestCase() {
+
+ open val useShelfRefactor: Boolean = false
+ open val useSensitiveReveal: Boolean = false
+ private val flags = FakeFeatureFlags()
@Mock
private lateinit var largeScreenShadeInterpolator: LargeScreenShadeInterpolator
@Mock
- private lateinit var flags: FeatureFlags
- @Mock
private lateinit var ambientState: AmbientState
@Mock
private lateinit var hostLayoutController: NotificationStackScrollLayoutController
+ @Mock
+ private lateinit var hostLayout: NotificationStackScrollLayout
+ @Mock
+ private lateinit var roundnessManager: NotificationRoundnessManager
private lateinit var shelf: NotificationShelf
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ mDependency.injectTestDependency(FeatureFlags::class.java, flags)
+ flags.set(Flags.NOTIFICATION_SHELF_REFACTOR, useShelfRefactor)
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, useSensitiveReveal)
+ flags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS)
val root = FrameLayout(context)
shelf = LayoutInflater.from(root.context)
.inflate(/* resource = */ R.layout.status_bar_notification_shelf,
@@ -57,10 +70,13 @@
/* attachToRoot = */false) as NotificationShelf
whenever(ambientState.largeScreenShadeInterpolator).thenReturn(largeScreenShadeInterpolator)
- whenever(ambientState.featureFlags).thenReturn(flags)
whenever(ambientState.isSmallScreen).thenReturn(true)
- shelf.bind(ambientState, /* hostLayoutController */ hostLayoutController)
+ if (useShelfRefactor) {
+ shelf.bind(ambientState, hostLayout, roundnessManager)
+ } else {
+ shelf.bind(ambientState, hostLayoutController)
+ }
shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
}
@@ -345,7 +361,7 @@
@Test
fun updateState_withNullLastVisibleBackgroundChild_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -372,7 +388,7 @@
@Test
fun updateState_withNullFirstViewInShelf_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -399,7 +415,7 @@
@Test
fun updateState_withCollapsedShade_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -426,7 +442,7 @@
@Test
fun updateState_withHiddenSectionBeforeShelf_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -486,3 +502,25 @@
assertEquals(expectedAlpha, shelf.viewState.alpha)
}
}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithRefactorTest : NotificationShelfTest() {
+ override val useShelfRefactor: Boolean = true
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithSensitiveRevealTest : NotificationShelfTest() {
+ override val useSensitiveReveal: Boolean = true
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithBothFlagsTest : NotificationShelfTest() {
+ override val useShelfRefactor: Boolean = true
+ override val useSensitiveReveal: Boolean = true
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index ee8325e..07eadf7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -54,7 +54,6 @@
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -119,6 +118,7 @@
@RunWith(AndroidTestingRunner.class)
public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationsController mNotificationsController;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@@ -157,7 +157,6 @@
@Mock private StackStateLogger mStackLogger;
@Mock private NotificationStackScrollLogger mLogger;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
- private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock private NotificationTargetsHelper mNotificationTargetsHelper;
@Mock private SecureSettings mSecureSettings;
@Mock private NotificationIconAreaController mIconAreaController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 8ad271b..72fcdec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -68,7 +68,9 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
@@ -106,6 +108,7 @@
@TestableLooper.RunWithLooper
public class NotificationStackScrollLayoutTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private NotificationStackScrollLayout mStackScroller; // Normally test this
private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below
private AmbientState mAmbientState;
@@ -129,7 +132,6 @@
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
- @Mock private FeatureFlags mFeatureFlags;
@Before
public void setUp() throws Exception {
@@ -143,11 +145,25 @@
mNotificationSectionsManager,
mBypassController,
mStatusBarKeyguardViewManager,
- mLargeScreenShadeInterpolator,
- mFeatureFlags
+ mLargeScreenShadeInterpolator
));
+ // Register the debug flags we use
+ assertFalse(Flags.NSSL_DEBUG_LINES.getDefault());
+ assertFalse(Flags.NSSL_DEBUG_REMOVE_ANIMATION.getDefault());
+ mFeatureFlags.set(Flags.NSSL_DEBUG_LINES, false);
+ mFeatureFlags.set(Flags.NSSL_DEBUG_REMOVE_ANIMATION, false);
+
+ // Register the feature flags we use
+ // TODO: Ideally we wouldn't need to set these unless a test actually reads them,
+ // and then we would test both configurations, but currently they are all read
+ // in the constructor.
+ mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
+ mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
+ mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR);
+
// Inject dependencies before initializing the layout
+ mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState);
mDependency.injectMockDependency(ShadeController.class);
mDependency.injectTestDependency(
@@ -176,13 +192,18 @@
mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper,
mNotificationStackSizeCalculator);
mStackScroller = spy(mStackScrollerInternal);
- mStackScroller.setShelfController(notificationShelfController);
+ if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ mStackScroller.setShelfController(notificationShelfController);
+ }
mStackScroller.setNotificationsController(mNotificationsController);
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
when(mStackScrollLayoutController.getNotificationRoundnessManager())
.thenReturn(mNotificationRoundnessManager);
mStackScroller.setController(mStackScrollLayoutController);
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ mStackScroller.setShelf(mNotificationShelf);
+ }
doNothing().when(mGroupExpansionManager).collapseGroups();
doNothing().when(mExpandHelper).cancelImmediately();
@@ -899,7 +920,6 @@
@Test
public void testWindowInsetAnimationProgress_updatesBottomInset() {
int bottomImeInset = 100;
- mStackScrollerInternal.setAnimatedInsetsEnabled(true);
WindowInsets windowInsets = new WindowInsets.Builder()
.setInsets(ime(), Insets.of(0, 0, 0, bottomImeInset)).build();
ArrayList<WindowInsetsAnimation> windowInsetsAnimations = new ArrayList<>();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index df65c09..85a2bdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -47,6 +47,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
@@ -83,7 +84,7 @@
private Handler mHandler;
private ExpandableNotificationRow mNotificationRow;
private Runnable mFalsingCheck;
- private FeatureFlags mFeatureFlags;
+ private final FeatureFlags mFeatureFlags = new FakeFeatureFlags();
private static final int FAKE_ROW_WIDTH = 20;
private static final int FAKE_ROW_HEIGHT = 20;
@@ -96,7 +97,6 @@
mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
- mFeatureFlags = mock(FeatureFlags.class);
mSwipeHelper = spy(new NotificationSwipeHelper(
mContext.getResources(),
ViewConfiguration.get(mContext),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
index 45725ce..e30947c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
@@ -18,6 +18,7 @@
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class NotificationTargetsHelperTest : SysuiTestCase() {
+ private val featureFlags = FakeFeatureFlags()
lateinit var notificationTestHelper: NotificationTestHelper
private val sectionsManager: NotificationSectionsManager = mock()
private val stackScrollLayout: NotificationStackScrollLayout = mock()
@@ -26,10 +27,10 @@
fun setUp() {
allowTestableLooperAsMainThread()
notificationTestHelper =
- NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ NotificationTestHelper(mContext, mDependency, TestableLooper.get(this), featureFlags)
}
- private fun notificationTargetsHelper() = NotificationTargetsHelper(FakeFeatureFlags())
+ private fun notificationTargetsHelper() = NotificationTargetsHelper(featureFlags)
@Test
fun targetsForFirstNotificationInGroup() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 4c97d20..987861d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -8,7 +8,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.EmptyShadeView
import com.android.systemui.statusbar.NotificationShelf
@@ -45,7 +44,6 @@
private val dumpManager = mock<DumpManager>()
private val mStatusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private val notificationShelf = mock<NotificationShelf>()
- private val featureFlags = mock<FeatureFlags>()
private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
}
@@ -56,7 +54,6 @@
/* bypassController */ { false },
mStatusBarKeyguardViewManager,
largeScreenShadeInterpolator,
- featureFlags,
)
private val testableResources = mContext.getOrCreateTestableResources()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 9c7f619..33144f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -64,7 +64,7 @@
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -117,6 +117,7 @@
public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
private static final int DISPLAY_ID = 0;
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock
private AssistManager mAssistManager;
@@ -256,7 +257,7 @@
notificationAnimationProvider,
mock(LaunchFullScreenIntentProvider.class),
mPowerInteractor,
- mock(FeatureFlags.class),
+ mFeatureFlags,
mUserTracker
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index cd8aaa2..9c52788 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -79,7 +79,6 @@
private CommandQueue mCommandQueue;
private FakeMetricsLogger mMetricsLogger;
private final ShadeController mShadeController = mock(ShadeController.class);
- private final CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class);
private final NotificationsInteractor mNotificationsInteractor =
mock(NotificationsInteractor.class);
private final KeyguardStateController mKeyguardStateController =
@@ -118,7 +117,6 @@
mock(NotificationShadeWindowController.class),
mock(DynamicPrivacyController.class),
mKeyguardStateController,
- mCentralSurfaces,
mNotificationsInteractor,
mock(LockscreenShadeTransitionController.class),
mock(PowerInteractor.class),
@@ -202,7 +200,6 @@
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
- when(mCentralSurfaces.isOccluded()).thenReturn(false);
assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 391c8ca..7c285b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -102,6 +102,8 @@
"com.android.sysuitest.dummynotificationsender";
private static final int DUMMY_MESSAGE_APP_ID = Process.LAST_APPLICATION_UID - 1;
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+
@Mock private RemoteInputController mController;
@Mock private ShortcutManager mShortcutManager;
@Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
@@ -453,8 +455,7 @@
private RemoteInputViewController bindController(
RemoteInputView view,
NotificationEntry entry) {
- FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
- fakeFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
+ mFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
RemoteInputViewControllerImpl viewController = new RemoteInputViewControllerImpl(
view,
entry,
@@ -462,7 +463,7 @@
mController,
mShortcutManager,
mUiEventLoggerFake,
- fakeFeatureFlags
+ mFeatureFlags
);
viewController.bind();
return viewController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 4839eeb..17bb73b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -92,7 +92,7 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -305,6 +305,7 @@
private TestableLooper mTestableLooper;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private UserHandle mUser0;
@@ -423,7 +424,7 @@
mCommonNotifCollection,
mNotifPipeline,
mSysUiState,
- mock(FeatureFlags.class),
+ mFeatureFlags,
mNotifPipelineFlags,
syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
@@ -432,7 +433,8 @@
mNotificationTestHelper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index b94f816e..36fa7e6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -62,6 +62,32 @@
}
}
+ /**
+ * Set the given flag's default value if no other value has been set.
+ *
+ * REMINDER: You should always test your code with your flag in both configurations, so
+ * generally you should be setting a particular value. This method should be reserved for
+ * situations where the flag needs to be read (e.g. in the class constructor), but its
+ * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
+ * this method than to hard-code `false` or `true` because then at least if you're wrong,
+ * and the flag value *does* matter, you'll notice when the flag is flipped and tests
+ * start failing.
+ */
+ fun setDefault(flag: BooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default)
+
+ /**
+ * Set the given flag's default value if no other value has been set.
+ *
+ * REMINDER: You should always test your code with your flag in both configurations, so
+ * generally you should be setting a particular value. This method should be reserved for
+ * situations where the flag needs to be read (e.g. in the class constructor), but its
+ * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
+ * this method than to hard-code `false` or `true` because then at least if you're wrong,
+ * and the flag value *does* matter, you'll notice when the flag is flipped and tests
+ * start failing.
+ */
+ fun setDefault(flag: SysPropBooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default)
+
private fun notifyFlagChanged(flag: Flag<*>) {
flagListeners[flag.id]?.let { listeners ->
listeners.forEach { listener ->
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 1482d07..3f3fa34 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -246,6 +246,31 @@
}
@GuardedBy("mLock")
+ boolean isAtEdge() {
+ return isAtLeftEdge() || isAtRightEdge() || isAtTopEdge() || isAtBottomEdge();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtLeftEdge() {
+ return getOffsetX() == getMaxOffsetXLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtRightEdge() {
+ return getOffsetX() == getMinOffsetXLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtTopEdge() {
+ return getOffsetY() == getMaxOffsetYLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtBottomEdge() {
+ return getOffsetY() == getMinOffsetYLocked();
+ }
+
+ @GuardedBy("mLock")
float getCenterX() {
return (mMagnificationBounds.width() / 2.0f
+ mMagnificationBounds.left - getOffsetX()) / getScale();
@@ -1086,6 +1111,87 @@
}
/**
+ * Returns whether the user is at one of the edges (left, right, top, bottom)
+ * of the magnification viewport
+ *
+ * @param displayId
+ * @return if user is at the edge of the view
+ */
+ public boolean isAtEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the left edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at left edge of view
+ */
+ public boolean isAtLeftEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtLeftEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the right edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at right edge of view
+ */
+ public boolean isAtRightEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtRightEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the top edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at top edge of view
+ */
+ public boolean isAtTopEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtTopEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the bottom edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at bottom edge of view
+ */
+ public boolean isAtBottomEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtBottomEdge();
+ }
+ }
+
+ /**
* Returns the Y offset of the magnification viewport. If an animation
* is in progress, this reflects the end state of the animation.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 038847e..4aebbf1 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -137,6 +137,7 @@
@VisibleForTesting final DetectingState mDetectingState;
@VisibleForTesting final PanningScalingState mPanningScalingState;
@VisibleForTesting final ViewportDraggingState mViewportDraggingState;
+ @VisibleForTesting final SinglePanningState mSinglePanningState;
private final ScreenStateReceiver mScreenStateReceiver;
private final WindowMagnificationPromptController mPromptController;
@@ -146,7 +147,7 @@
private PointerCoords[] mTempPointerCoords;
private PointerProperties[] mTempPointerProperties;
-
+ @VisibleForTesting boolean mIsSinglePanningEnabled;
public FullScreenMagnificationGestureHandler(@UiContext Context context,
FullScreenMagnificationController fullScreenMagnificationController,
AccessibilityTraceManager trace,
@@ -202,6 +203,8 @@
mDetectingState = new DetectingState(context);
mViewportDraggingState = new ViewportDraggingState();
mPanningScalingState = new PanningScalingState(context);
+ mSinglePanningState = new SinglePanningState(context);
+ setSinglePanningEnabled(false);
if (mDetectShortcutTrigger) {
mScreenStateReceiver = new ScreenStateReceiver(context, this);
@@ -213,6 +216,11 @@
transitionTo(mDetectingState);
}
+ @VisibleForTesting
+ void setSinglePanningEnabled(boolean isEnabled) {
+ mIsSinglePanningEnabled = isEnabled;
+ }
+
@Override
void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
handleEventWith(mCurrentState, event, rawEvent, policyFlags);
@@ -223,6 +231,7 @@
// To keep InputEventConsistencyVerifiers within GestureDetectors happy
mPanningScalingState.mScrollGestureDetector.onTouchEvent(event);
mPanningScalingState.mScaleGestureDetector.onTouchEvent(event);
+ mSinglePanningState.mScrollGestureDetector.onTouchEvent(event);
try {
stateHandler.onMotionEvent(event, rawEvent, policyFlags);
@@ -669,7 +678,6 @@
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-
// Ensures that the state at the end of delegation is consistent with the last delegated
// UP/DOWN event in queue: still delegating if pointer is down, detecting otherwise
switch (event.getActionMasked()) {
@@ -726,6 +734,8 @@
@VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper(), this);
+ private PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+
DetectingState(Context context) {
mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
@@ -765,10 +775,11 @@
cacheDelayedMotionEvent(event, rawEvent, policyFlags);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
-
mLastDetectingDownEventTime = event.getDownTime();
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ mFirstPointerDownLocation.set(event.getX(), event.getY());
+
if (!mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, event.getX(), event.getY())) {
@@ -800,7 +811,7 @@
break;
case ACTION_POINTER_DOWN: {
if (isActivated() && event.getPointerCount() == 2) {
- storeSecondPointerDownLocation(event);
+ storePointerDownLocation(mSecondPointerDownLocation, event);
mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
ViewConfiguration.getTapTimeout());
} else {
@@ -815,7 +826,6 @@
case ACTION_MOVE: {
if (isFingerDown()
&& distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
-
// Swipe detected - transition immediately
// For convenience, viewport dragging takes precedence
@@ -826,10 +836,15 @@
} else if (isActivated() && event.getPointerCount() == 2) {
//Primary pointer is swiping, so transit to PanningScalingState
transitToPanningScalingStateAndClear();
+ } else if (mIsSinglePanningEnabled
+ && isActivated()
+ && event.getPointerCount() == 1
+ && !isOverscroll(event)) {
+ transitToSinglePanningStateAndClear();
} else {
transitionToDelegatingStateAndClear();
}
- } else if (isActivated() && secondPointerDownValid()
+ } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
&& distanceClosestPointerToPoint(
mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
//Second pointer is swiping, so transit to PanningScalingState
@@ -843,11 +858,9 @@
if (!mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, event.getX(), event.getY())) {
-
transitionToDelegatingStateAndClear();
} else if (isMultiTapTriggered(3 /* taps */)) {
-
onTripleTap(/* up */ event);
} else if (
@@ -856,7 +869,6 @@
//TODO long tap should never happen here
&& ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay)
|| (distance(mLastDown, mLastUp) >= mSwipeMinDistance))) {
-
transitionToDelegatingStateAndClear();
}
@@ -865,14 +877,28 @@
}
}
- private void storeSecondPointerDownLocation(MotionEvent event) {
- final int index = event.getActionIndex();
- mSecondPointerDownLocation.set(event.getX(index), event.getY(index));
+ private boolean isOverscroll(MotionEvent event) {
+ if (!pointerDownValid(mFirstPointerDownLocation)) {
+ return false;
+ }
+ float dX = event.getX() - mFirstPointerDownLocation.x;
+ float dY = event.getY() - mFirstPointerDownLocation.y;
+ boolean didOverscroll =
+ mFullScreenMagnificationController.isAtLeftEdge(mDisplayId) && dX > 0
+ || mFullScreenMagnificationController.isAtRightEdge(mDisplayId) && dX < 0
+ || mFullScreenMagnificationController.isAtTopEdge(mDisplayId) && dY > 0
+ || mFullScreenMagnificationController.isAtBottomEdge(mDisplayId) && dY < 0;
+ return didOverscroll;
}
- private boolean secondPointerDownValid() {
- return !(Float.isNaN(mSecondPointerDownLocation.x) && Float.isNaN(
- mSecondPointerDownLocation.y));
+ private void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) {
+ final int index = event.getActionIndex();
+ pointerDownLocation.set(event.getX(index), event.getY(index));
+ }
+
+ private boolean pointerDownValid(PointF pointerDownLocation) {
+ return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(
+ pointerDownLocation.y));
}
private void transitToPanningScalingStateAndClear() {
@@ -880,6 +906,11 @@
clear();
}
+ private void transitToSinglePanningStateAndClear() {
+ transitionTo(mSinglePanningState);
+ clear();
+ }
+
public boolean isMultiTapTriggered(int numTaps) {
// Shortcut acts as the 2 initial taps
@@ -947,6 +978,7 @@
setShortcutTriggered(false);
removePendingDelayedMessages();
clearDelayedMotionEvents();
+ mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
}
@@ -1165,12 +1197,14 @@
+ ", mDelegatingState=" + mDelegatingState
+ ", mMagnifiedInteractionState=" + mPanningScalingState
+ ", mViewportDraggingState=" + mViewportDraggingState
+ + ", mSinglePanningState=" + mSinglePanningState
+ ", mDetectTripleTap=" + mDetectTripleTap
+ ", mDetectShortcutTrigger=" + mDetectShortcutTrigger
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)
+ ", mMagnificationController=" + mFullScreenMagnificationController
+ ", mDisplayId=" + mDisplayId
+ + ", mIsSinglePanningEnabled=" + mIsSinglePanningEnabled
+ '}';
}
@@ -1285,8 +1319,67 @@
* Indicates an error with a gesture handler or state.
*/
private static class GestureException extends Exception {
+
GestureException(String message) {
super(message);
}
}
+
+ final class SinglePanningState extends SimpleOnGestureListener implements State {
+ private final GestureDetector mScrollGestureDetector;
+ private MotionEventInfo mEvent;
+
+ SinglePanningState(Context context) {
+ mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ int action = event.getActionMasked();
+ switch (action) {
+ case ACTION_UP:
+ case ACTION_CANCEL:
+ clear();
+ transitionTo(mDetectingState);
+ break;
+ }
+ }
+
+ @Override
+ public boolean onScroll(
+ MotionEvent first, MotionEvent second, float distanceX, float distanceY) {
+ if (mCurrentState != mSinglePanningState) {
+ return true;
+ }
+ mFullScreenMagnificationController.offsetMagnifiedRegion(
+ mDisplayId,
+ distanceX,
+ distanceY,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ if (DEBUG_PANNING_SCALING) {
+ Slog.i(
+ mLogTag,
+ "SinglePanningState Panned content by scrollX: "
+ + distanceX
+ + " scrollY: "
+ + distanceY
+ + " isAtEdge: "
+ + mFullScreenMagnificationController.isAtEdge(mDisplayId));
+ }
+ // TODO: b/280812104 Dispatch events before Delegation
+ if (mFullScreenMagnificationController.isAtEdge(mDisplayId)) {
+ clear();
+ transitionTo(mDelegatingState);
+ }
+ return /* event consumed: */ true;
+ }
+
+ @Override
+ public String toString() {
+ return "SinglePanningState{"
+ + "isEdgeOfView="
+ + mFullScreenMagnificationController.isAtEdge(mDisplayId)
+ + "}";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2db724f..8e1ad65 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2491,16 +2491,6 @@
getContext().registerReceiver(mReviewNotificationPermissionsReceiver,
ReviewNotificationPermissionsReceiver.getFilter(),
Context.RECEIVER_NOT_EXPORTED);
-
- mAppOps.startWatchingMode(AppOpsManager.OP_POST_NOTIFICATION, null,
- new AppOpsManager.OnOpChangedInternalListener() {
- @Override
- public void onOpChanged(@NonNull String op, @NonNull String packageName,
- int userId) {
- mHandler.post(
- () -> handleNotificationPermissionChange(packageName, userId));
- }
- });
}
/**
@@ -3560,16 +3550,20 @@
}
mPermissionHelper.setNotificationPermission(
pkg, UserHandle.getUserId(uid), enabled, true);
+ sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
.setType(MetricsEvent.TYPE_ACTION)
.setPackageName(pkg)
.setSubtype(enabled ? 1 : 0));
mNotificationChannelLogger.logAppNotificationsAllowed(uid, pkg, enabled);
+ // Now, cancel any outstanding notifications that are part of a just-disabled app
+ if (!enabled) {
+ cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0,
+ UserHandle.getUserId(uid), REASON_PACKAGE_BANNED);
+ }
- // Outstanding notifications from this package will be cancelled, and the package will
- // be sent the ACTION_APP_BLOCK_STATE_CHANGED broadcast, as soon as we get the
- // callback from AppOpsManager.
+ handleSavePolicyFile();
}
/**
@@ -5889,21 +5883,6 @@
}
};
- private void handleNotificationPermissionChange(String pkg, @UserIdInt int userId) {
- int uid = mPackageManagerInternal.getPackageUid(pkg, 0, userId);
- if (uid == INVALID_UID) {
- Log.e(TAG, String.format("No uid found for %s, %s!", pkg, userId));
- return;
- }
- boolean hasPermission = mPermissionHelper.hasPermission(uid);
- sendAppBlockStateChangedBroadcast(pkg, uid, !hasPermission);
- if (!hasPermission) {
- cancelAllNotificationsInt(MY_UID, MY_PID, pkg, /* channelId= */ null,
- /* mustHaveFlags= */ 0, /* mustNotHaveFlags= */ 0, userId,
- REASON_PACKAGE_BANNED);
- }
- }
-
protected void checkNotificationListenerAccess() {
if (!isCallerSystemOrPhone()) {
getContext().enforceCallingPermission(
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 36a0b0c..1f5bd3e 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -75,6 +75,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.logging.MetricsLogger;
@@ -108,30 +109,34 @@
static final int RULE_LIMIT_PER_PACKAGE = 100;
// pkg|userId => uid
- protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
+ @VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
private final Context mContext;
private final H mHandler;
private final SettingsObserver mSettingsObserver;
private final AppOpsManager mAppOps;
- @VisibleForTesting protected final NotificationManager mNotificationManager;
+ private final NotificationManager mNotificationManager;
private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
- @VisibleForTesting protected ZenModeConfig mDefaultConfig;
+ private ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final ZenModeFiltering mFiltering;
- protected final RingerModeDelegate mRingerModeDelegate = new
+ private final RingerModeDelegate mRingerModeDelegate = new
RingerModeDelegate();
@VisibleForTesting protected final ZenModeConditions mConditions;
- Object mConfigsLock = new Object();
+ private final Object mConfigsArrayLock = new Object();
+ @GuardedBy("mConfigsArrayLock")
@VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
private final Metrics mMetrics = new Metrics();
private final ConditionProviders.Config mServiceConfig;
- private SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
- @VisibleForTesting protected ZenModeEventLogger mZenModeEventLogger;
+ private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
+ private final ZenModeEventLogger mZenModeEventLogger;
@VisibleForTesting protected int mZenMode;
@VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
private int mUser = UserHandle.USER_SYSTEM;
+
+ private final Object mConfigLock = new Object();
+ @GuardedBy("mConfigLock")
@VisibleForTesting protected ZenModeConfig mConfig;
@VisibleForTesting protected AudioManagerInternal mAudioManager;
protected PackageManager mPm;
@@ -159,7 +164,7 @@
mDefaultConfig = readDefaultConfig(mContext.getResources());
updateDefaultAutomaticRuleNames();
mConfig = mDefaultConfig.copy();
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
}
mConsolidatedPolicy = mConfig.toNotificationPolicy();
@@ -186,7 +191,7 @@
public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity,
int callingUid) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConsolidatedPolicy,
userHandle, extras, validator, contactsTimeoutMs, timeoutAffinity,
callingUid);
@@ -206,7 +211,7 @@
}
public boolean shouldIntercept(NotificationRecord record) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return mFiltering.shouldIntercept(mZenMode, mConsolidatedPolicy, record);
}
}
@@ -221,7 +226,7 @@
public void initZenMode() {
if (DEBUG) Log.d(TAG, "initZenMode");
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
// "update" config to itself, which will have no effect in the case where a config
// was read in via XML, but will initialize zen mode if nothing was read in and the
// config remains the default.
@@ -250,7 +255,7 @@
public void onUserRemoved(int user) {
if (user < UserHandle.USER_SYSTEM) return;
if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user);
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.remove(user);
}
}
@@ -268,7 +273,7 @@
mUser = user;
if (DEBUG) Log.d(TAG, reason + " u=" + user);
ZenModeConfig config = null;
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
if (mConfigs.get(user) != null) {
config = mConfigs.get(user).copy();
}
@@ -278,7 +283,7 @@
config = mDefaultConfig.copy();
config.user = user;
}
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
cleanUpZenRules();
@@ -314,7 +319,7 @@
public List<ZenRule> getZenRules() {
List<ZenRule> rules = new ArrayList<>();
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return rules;
for (ZenRule rule : mConfig.automaticRules.values()) {
if (canManageAutomaticZenRule(rule)) {
@@ -327,7 +332,7 @@
public AutomaticZenRule getAutomaticZenRule(String id) {
ZenRule rule;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return null;
rule = mConfig.automaticRules.get(id);
}
@@ -364,7 +369,7 @@
}
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) {
throw new AndroidRuntimeException("Could not create rule");
}
@@ -387,7 +392,7 @@
public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
String reason, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
if (DEBUG) {
Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
@@ -419,7 +424,7 @@
public boolean removeAutomaticZenRule(String id, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
newConfig = mConfig.copy();
ZenRule ruleToRemove = newConfig.automaticRules.get(id);
@@ -450,7 +455,7 @@
public boolean removeAutomaticZenRules(String packageName, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
newConfig = mConfig.copy();
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -467,7 +472,7 @@
public void setAutomaticZenRuleState(String id, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
newConfig = mConfig.copy();
@@ -481,7 +486,7 @@
public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
newConfig = mConfig.copy();
@@ -491,6 +496,7 @@
}
}
+ @GuardedBy("mConfigLock")
private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules,
Condition condition, int callingUid, boolean fromSystemOrSystemUi) {
if (rules == null || rules.isEmpty()) return;
@@ -538,7 +544,7 @@
return 0;
}
int count = 0;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
for (ZenRule rule : mConfig.automaticRules.values()) {
if (cn.equals(rule.component) || cn.equals(rule.configurationActivity)) {
count++;
@@ -555,7 +561,7 @@
return 0;
}
int count = 0;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
for (ZenRule rule : mConfig.automaticRules.values()) {
if (pkg.equals(rule.getPkg())) {
count++;
@@ -588,19 +594,23 @@
protected void updateDefaultZenRules(int callingUid, boolean fromSystemOrSystemUi) {
updateDefaultAutomaticRuleNames();
- for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
- ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
- // if default rule wasn't user-modified nor enabled, use localized name
- // instead of previous system name
- if (currRule != null && !currRule.modified && !currRule.enabled
- && !defaultRule.name.equals(currRule.name)) {
- if (canManageAutomaticZenRule(currRule)) {
- if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name "
- + "from " + currRule.name + " to " + defaultRule.name);
- // update default rule (if locale changed, name of rule will change)
- currRule.name = defaultRule.name;
- updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
- "locale changed", callingUid, fromSystemOrSystemUi);
+ synchronized (mConfigLock) {
+ for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
+ ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
+ // if default rule wasn't user-modified nor enabled, use localized name
+ // instead of previous system name
+ if (currRule != null && !currRule.modified && !currRule.enabled
+ && !defaultRule.name.equals(currRule.name)) {
+ if (canManageAutomaticZenRule(currRule)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Locale change - updating default zen rule name "
+ + "from " + currRule.name + " to " + defaultRule.name);
+ }
+ // update default rule (if locale changed, name of rule will change)
+ currRule.name = defaultRule.name;
+ updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
+ "locale changed", callingUid, fromSystemOrSystemUi);
+ }
}
}
}
@@ -686,7 +696,7 @@
private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
if (!Global.isValidZenMode(zenMode)) return;
if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
@@ -715,7 +725,7 @@
void dump(ProtoOutputStream proto) {
proto.write(ZenModeProto.ZEN_MODE, mZenMode);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig.manualRule != null) {
mConfig.manualRule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
@@ -737,14 +747,14 @@
pw.println(Global.zenModeToString(mZenMode));
pw.print(prefix);
pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
- synchronized(mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int N = mConfigs.size();
for (int i = 0; i < N; i++) {
dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
}
}
pw.print(prefix); pw.print("mUser="); pw.println(mUser);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
dump(pw, prefix, "mConfig", mConfig);
}
@@ -833,7 +843,7 @@
Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId);
}
if (DEBUG) Log.d(TAG, reason);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
}
@@ -841,7 +851,7 @@
public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
throws IOException {
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int n = mConfigs.size();
for (int i = 0; i < n; i++) {
if (forBackup && mConfigs.keyAt(i) != userId) {
@@ -856,7 +866,9 @@
* @return user-specified default notification policy for priority only do not disturb
*/
public Policy getNotificationPolicy() {
- return getNotificationPolicy(mConfig);
+ synchronized (mConfigLock) {
+ return getNotificationPolicy(mConfig);
+ }
}
private static Policy getNotificationPolicy(ZenModeConfig config) {
@@ -867,8 +879,8 @@
* Sets the global notification policy used for priority only do not disturb
*/
public void setNotificationPolicy(Policy policy, int callingUid, boolean fromSystemOrSystemUi) {
- if (policy == null || mConfig == null) return;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (policy == null || mConfig == null) return;
final ZenModeConfig newConfig = mConfig.copy();
newConfig.applyNotificationPolicy(policy);
setConfigLocked(newConfig, null, "setNotificationPolicy", callingUid,
@@ -881,7 +893,7 @@
*/
private void cleanUpZenRules() {
long currentTime = System.currentTimeMillis();
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
final ZenModeConfig newConfig = mConfig.copy();
if (newConfig.automaticRules != null) {
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -906,7 +918,7 @@
* @return a copy of the zen mode configuration
*/
public ZenModeConfig getConfig() {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return mConfig.copy();
}
}
@@ -918,7 +930,8 @@
return mConsolidatedPolicy.copy();
}
- public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
+ @GuardedBy("mConfigLock")
+ private boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
String reason, int callingUid, boolean fromSystemOrSystemUi) {
return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/,
callingUid, fromSystemOrSystemUi);
@@ -926,11 +939,12 @@
public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason,
int callingUid, boolean fromSystemOrSystemUi) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, triggeringComponent, reason, callingUid, fromSystemOrSystemUi);
}
}
+ @GuardedBy("mConfigLock")
private boolean setConfigLocked(ZenModeConfig config, String reason,
ComponentName triggeringComponent, boolean setRingerMode, int callingUid,
boolean fromSystemOrSystemUi) {
@@ -942,7 +956,7 @@
}
if (config.user != mUser) {
// simply store away for background users
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(config.user, config);
}
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
@@ -951,7 +965,7 @@
// handle CPS backed conditions - danger! may modify config
mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(config.user, config);
}
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
@@ -979,6 +993,7 @@
* Carries out a config update (if needed) and (re-)evaluates the zen mode value afterwards.
* If logging is enabled, will also request logging of the outcome of this change if needed.
*/
+ @GuardedBy("mConfigLock")
private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason,
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
final boolean logZenModeEvents = mFlagResolver.isEnabled(
@@ -993,7 +1008,7 @@
}
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
- evaluateZenMode(reason, setRingerMode);
+ evaluateZenModeLocked(reason, setRingerMode);
// After all changes have occurred, log if requested
if (logZenModeEvents) {
ZenModeEventLogger.ZenModeInfo newInfo = new ZenModeEventLogger.ZenModeInfo(
@@ -1025,7 +1040,8 @@
}
@VisibleForTesting
- protected void evaluateZenMode(String reason, boolean setRingerMode) {
+ @GuardedBy("mConfigLock")
+ protected void evaluateZenModeLocked(String reason, boolean setRingerMode) {
if (DEBUG) Log.d(TAG, "evaluateZenMode");
if (mConfig == null) return;
final int policyHashBefore = mConsolidatedPolicy == null ? 0
@@ -1056,8 +1072,8 @@
}
private int computeZenMode() {
- if (mConfig == null) return Global.ZEN_MODE_OFF;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) return Global.ZEN_MODE_OFF;
if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
int zen = Global.ZEN_MODE_OFF;
for (ZenRule automaticRule : mConfig.automaticRules.values()) {
@@ -1094,8 +1110,8 @@
}
private void updateConsolidatedPolicy(String reason) {
- if (mConfig == null) return;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) return;
ZenPolicy policy = new ZenPolicy();
if (mConfig.manualRule != null) {
applyCustomPolicy(policy, mConfig.manualRule);
@@ -1293,7 +1309,7 @@
* Generate pulled atoms about do not disturb configurations.
*/
public void pullRules(List<StatsEvent> events) {
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int numConfigs = mConfigs.size();
for (int i = 0; i < numConfigs; i++) {
final int user = mConfigs.keyAt(i);
@@ -1319,6 +1335,7 @@
}
}
+ @GuardedBy("mConfigsArrayLock")
private void ruleToProtoLocked(int user, ZenRule rule, boolean isManualRule,
List<StatsEvent> events) {
// Make the ID safe.
@@ -1389,7 +1406,7 @@
if (mZenMode == Global.ZEN_MODE_OFF
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig))) {
+ && !areAllPriorityOnlyRingerSoundsMuted())) {
// in priority only with ringer not muted, save ringer mode changes
// in dnd off, save ringer mode changes
setPreviousRingerModeSetting(ringerModeNew);
@@ -1410,8 +1427,7 @@
&& (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
|| mZenMode == Global.ZEN_MODE_ALARMS
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
- mConfig)))) {
+ && areAllPriorityOnlyRingerSoundsMuted()))) {
newZen = Global.ZEN_MODE_OFF;
} else if (mZenMode != Global.ZEN_MODE_OFF) {
ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT;
@@ -1430,6 +1446,12 @@
return ringerModeExternalOut;
}
+ private boolean areAllPriorityOnlyRingerSoundsMuted() {
+ synchronized (mConfigLock) {
+ return ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig);
+ }
+ }
+
@Override
public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller,
int ringerModeInternal, VolumePolicy policy) {
@@ -1633,7 +1655,7 @@
private void emitRules() {
final long now = SystemClock.elapsedRealtime();
final long since = (now - mRuleCountLogTime);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
int numZenRules = mConfig.automaticRules.size();
if (mNumZenRules != numZenRules
|| since > MINIMUM_LOG_PERIOD_MS) {
@@ -1651,7 +1673,7 @@
private void emitDndType() {
final long now = SystemClock.elapsedRealtime();
final long since = (now - mTypeLogTimeMs);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
boolean dndOn = mZenMode != Global.ZEN_MODE_OFF;
int zenType = !dndOn ? DND_OFF
: (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC;
diff --git a/services/core/java/com/android/server/pm/ArchiveManager.java b/services/core/java/com/android/server/pm/ArchiveManager.java
index 99479f0..5435206 100644
--- a/services/core/java/com/android/server/pm/ArchiveManager.java
+++ b/services/core/java/com/android/server/pm/ArchiveManager.java
@@ -77,9 +77,8 @@
snapshot.enforceCrossUserPermission(callingUid, userId, true, true,
"archiveApp");
verifyCaller(callerPackageName, callingPackageName);
-
PackageStateInternal ps = getPackageState(packageName, snapshot, callingUid, user);
- verifyInstallOwnership(packageName, callingPackageName, ps.getInstallSource());
+ verifyInstaller(packageName, ps.getInstallSource());
List<LauncherActivityInfo> mainActivities = getLauncherApps().getActivityList(
ps.getPackageName(),
@@ -125,7 +124,7 @@
Path.of("/TODO"), null);
activityInfos.add(activityInfo);
}
- // TODO(b/278553670) Adapt installer check verifyInstallOwnership and check for null there
+
InstallSource installSource = ps.getInstallSource();
String installerPackageName = installSource.mUpdateOwnerPackageName != null
? installSource.mUpdateOwnerPackageName : installSource.mInstallerPackageName;
@@ -159,19 +158,13 @@
}
}
- private static void verifyInstallOwnership(String packageName, String callingPackageName,
- InstallSource installSource) {
- if (!TextUtils.equals(installSource.mInstallerPackageName,
- callingPackageName)) {
+ private static void verifyInstaller(String packageName, InstallSource installSource) {
+ // TODO(b/291060290) Verify installer supports unarchiving
+ if (installSource.mUpdateOwnerPackageName == null
+ && installSource.mInstallerPackageName == null) {
throw new SecurityException(
- TextUtils.formatSimple("Caller is not the installer of record for %s.",
+ TextUtils.formatSimple("No installer found to archive app %s.",
packageName));
}
- String updateOwnerPackageName = installSource.mUpdateOwnerPackageName;
- if (updateOwnerPackageName != null
- && !TextUtils.equals(updateOwnerPackageName, callingPackageName)) {
- throw new SecurityException(
- TextUtils.formatSimple("Caller is not the update owner for %s.", packageName));
- }
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index ead26cc..134b041 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -4311,7 +4311,8 @@
// - It's an APEX or overlay package since stopped state does not affect them.
// - It is enumerated with a <initial-package-state> tag having the stopped attribute
// set to false
- // - It doesn't have a launcher entry which means the user doesn't have a way to unstop it
+ // - It doesn't have an enabled and exported launcher activity, which means the user
+ // wouldn't have a way to un-stop it
final boolean isApexPkg = (scanFlags & SCAN_AS_APEX) != 0;
if (mPm.mShouldStopSystemPackagesByDefault
&& scanSystemPartition
@@ -4337,7 +4338,11 @@
categories.add(Intent.CATEGORY_LAUNCHER);
final List<ParsedActivity> activities = parsedPackage.getActivities();
for (int indexActivity = 0; indexActivity < activities.size(); indexActivity++) {
- final List<ParsedIntentInfo> intents = activities.get(indexActivity).getIntents();
+ final ParsedActivity activity = activities.get(indexActivity);
+ if (!activity.isEnabled() || !activity.isExported()) {
+ continue;
+ }
+ final List<ParsedIntentInfo> intents = activity.getIntents();
for (int indexIntent = 0; indexIntent < intents.size(); indexIntent++) {
final IntentFilter intentFilter = intents.get(indexIntent).getIntentFilter();
if (intentFilter != null && intentFilter.matchCategories(categories) == null) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 0c1f33c..92c0987 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1020,7 +1020,7 @@
final WindowContainer<?> parent = getParent();
final Task thisTask = asTask();
if (thisTask != null && parent.asTask() == null
- && mTransitionController.isTransientHide(thisTask)) {
+ && mTransitionController.isTransientVisible(thisTask)) {
// Keep transient-hide root tasks visible. Non-root tasks still follow standard rule.
return TASK_FRAGMENT_VISIBILITY_VISIBLE;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a143540..eaea53d 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -407,6 +407,36 @@
return false;
}
+ /** Returns {@code true} if the task should keep visible if this is a transient transition. */
+ boolean isTransientVisible(@NonNull Task task) {
+ if (mTransientLaunches == null) return false;
+ int occludedCount = 0;
+ final int numTransient = mTransientLaunches.size();
+ for (int i = numTransient - 1; i >= 0; --i) {
+ final Task transientRoot = mTransientLaunches.keyAt(i).getRootTask();
+ if (transientRoot == null) continue;
+ final WindowContainer<?> rootParent = transientRoot.getParent();
+ if (rootParent == null || rootParent.getTopChild() == transientRoot) continue;
+ final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor
+ .mOpaqueActivityHelper.getOpaqueActivity(rootParent);
+ if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) {
+ occludedCount++;
+ }
+ }
+ if (occludedCount == numTransient) {
+ for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
+ if (mTransientLaunches.keyAt(i).isDescendantOf(task)) {
+ // Keep transient activity visible until transition finished, so it won't pause
+ // with transient-hide tasks that may delay resuming the next top.
+ return true;
+ }
+ }
+ // Let transient-hide activities pause before transition is finished.
+ return false;
+ }
+ return isInTransientHide(task);
+ }
+
boolean canApplyDim(@NonNull Task task) {
if (mTransientLaunches == null) return true;
final Dimmer dimmer = task.getDimmer();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index bfaf6fc..dfaa174 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -477,15 +477,22 @@
if (mCollectingTransition != null && mCollectingTransition.isInTransientHide(task)) {
return true;
}
- for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
- if (mWaitingTransitions.get(i).isInTransientHide(task)) return true;
- }
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
if (mPlayingTransitions.get(i).isInTransientHide(task)) return true;
}
return false;
}
+ boolean isTransientVisible(@NonNull Task task) {
+ if (mCollectingTransition != null && mCollectingTransition.isTransientVisible(task)) {
+ return true;
+ }
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ if (mPlayingTransitions.get(i).isTransientVisible(task)) return true;
+ }
+ return false;
+ }
+
boolean canApplyDim(@Nullable Task task) {
if (task == null) {
// Always allow non-activity window.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
index 7b16545..a8b0a7b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
@@ -40,6 +40,7 @@
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.text.TextUtils;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -169,14 +170,14 @@
}
@Test
- public void archiveApp_callerNotInstallerOfRecord() {
+ public void archiveApp_noInstallerFound() {
InstallSource otherInstallSource =
InstallSource.create(
CALLER_PACKAGE,
CALLER_PACKAGE,
- /* installerPackageName= */ "different",
+ /* installerPackageName= */ null,
Binder.getCallingUid(),
- CALLER_PACKAGE,
+ /* updateOwnerPackageName= */ null,
/* installerAttributionTag= */ null,
/* packageSource= */ 0);
when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
@@ -187,29 +188,8 @@
mIntentSender)
);
assertThat(e).hasMessageThat().isEqualTo(
- String.format("Caller is not the installer of record for %s.", PACKAGE));
- }
-
- @Test
- public void archiveApp_callerNotUpdateOwner() {
- InstallSource otherInstallSource =
- InstallSource.create(
- CALLER_PACKAGE,
- CALLER_PACKAGE,
- CALLER_PACKAGE,
- Binder.getCallingUid(),
- /* updateOwnerPackageName= */ "different",
- /* installerAttributionTag= */ null,
- /* packageSource= */ 0);
- when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
-
- Exception e = assertThrows(
- SecurityException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT,
- mIntentSender)
- );
- assertThat(e).hasMessageThat().isEqualTo(
- String.format("Caller is not the update owner for %s.", PACKAGE));
+ TextUtils.formatSimple("No installer found to archive app %s.",
+ PACKAGE));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 32d0c98..989aee0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -33,6 +33,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -42,6 +43,8 @@
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
@@ -67,11 +70,13 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.List;
@@ -123,9 +128,10 @@
public static final int STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP = 8;
public static final int STATE_PANNING = 9;
public static final int STATE_SCALING_AND_PANNING = 10;
+ public static final int STATE_SINGLE_PANNING = 11;
public static final int FIRST_STATE = STATE_IDLE;
- public static final int LAST_STATE = STATE_SCALING_AND_PANNING;
+ public static final int LAST_STATE = STATE_SINGLE_PANNING;
// Co-prime x and y, to potentially catch x-y-swapped errors
public static final float DEFAULT_X = 301;
@@ -155,6 +161,10 @@
private float mOriginalMagnificationPersistedScale;
+ static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 800, 800);
+
+ static final Region INITIAL_MAGNIFICATION_REGION = new Region(INITIAL_MAGNIFICATION_BOUNDS);
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -182,11 +192,19 @@
new MagnificationScaleProvider(mContext),
() -> null,
ConcurrentUtils.DIRECT_EXECUTOR) {
- @Override
- public boolean magnificationRegionContains(int displayId, float x, float y) {
- return true;
- }
+ @Override
+ public boolean magnificationRegionContains(int displayId, float x, float y) {
+ return true;
+ }
};
+
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ Object[] args = invocationOnMock.getArguments();
+ Region regionArg = (Region) args[1];
+ regionArg.set(new Rect(INITIAL_MAGNIFICATION_BOUNDS));
+ return null;
+ }).when(mockWindowManager).getMagnificationRegion(anyInt(), any(Region.class));
+
mFullScreenMagnificationController.register(DISPLAY_0);
mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(true);
mClock = new OffsettableClock.Stopped();
@@ -214,6 +232,7 @@
mContext, mFullScreenMagnificationController, mMockTraceManager, mMockCallback,
detectTripleTap, detectShortcutTrigger,
mWindowMagnificationPromptController, DISPLAY_0);
+ h.setSinglePanningEnabled(true);
mHandler = new TestHandler(h.mDetectingState, mClock) {
@Override
protected String messageToString(Message m) {
@@ -239,6 +258,7 @@
* {@link #returnToNormalFrom} (for navigating back to {@link #STATE_IDLE})
*/
@Test
+ @Ignore("b/291925580")
public void testEachState_isReachableAndRecoverable() {
forEachState(state -> {
goFromStateIdleTo(state);
@@ -526,6 +546,75 @@
}
@Test
+ public void testActionUpNotAtEdge_singlePanningState_detectingState() {
+ goFromStateIdleTo(STATE_SINGLE_PANNING);
+
+ send(upEvent());
+
+ check(mMgh.mCurrentState == mMgh.mDetectingState, STATE_IDLE);
+ assertTrue(isZoomed());
+ }
+
+ @Test
+ public void testScroll_SinglePanningDisabled_delegatingState() {
+ mMgh.setSinglePanningEnabled(false);
+
+ goFromStateIdleTo(STATE_ACTIVATED);
+ allowEventDelegation();
+ swipeAndHold();
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ }
+
+ @Test
+ public void testScroll_zoomedStateAndAtEdge_delegatingState() {
+ goFromStateIdleTo(STATE_ACTIVATED);
+ mFullScreenMagnificationController.setCenter(
+ DISPLAY_0,
+ INITIAL_MAGNIFICATION_BOUNDS.left,
+ INITIAL_MAGNIFICATION_BOUNDS.top / 2,
+ false,
+ 1);
+ final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+ PointF initCoords =
+ new PointF(
+ mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+ mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+ PointF endCoords = new PointF(initCoords.x, initCoords.y);
+ endCoords.offset(swipeMinDistance, 0);
+ allowEventDelegation();
+
+ swipeAndHold(initCoords, endCoords);
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ assertTrue(isZoomed());
+ }
+
+ @Test
+ public void testScroll_singlePanningAndAtEdge_delegatingState() {
+ goFromStateIdleTo(STATE_SINGLE_PANNING);
+ mFullScreenMagnificationController.setCenter(
+ DISPLAY_0,
+ INITIAL_MAGNIFICATION_BOUNDS.left,
+ INITIAL_MAGNIFICATION_BOUNDS.top / 2,
+ false,
+ 1);
+ final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+ PointF initCoords =
+ new PointF(
+ mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+ mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+ PointF endCoords = new PointF(initCoords.x, initCoords.y);
+ endCoords.offset(swipeMinDistance, 0);
+ allowEventDelegation();
+
+ swipeAndHold(initCoords, endCoords);
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ assertTrue(isZoomed());
+ }
+
+ @Test
public void testShortcutTriggered_invokeShowWindowPromptAction() {
goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
@@ -740,6 +829,10 @@
state);
check(mMgh.mPanningScalingState.mScaling, state);
} break;
+ case STATE_SINGLE_PANNING: {
+ check(isZoomed(), state);
+ check(mMgh.mCurrentState == mMgh.mSinglePanningState, state);
+ } break;
default: throw new IllegalArgumentException("Illegal state: " + state);
}
}
@@ -803,6 +896,10 @@
send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 4));
send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 5));
} break;
+ case STATE_SINGLE_PANNING: {
+ goFromStateIdleTo(STATE_ACTIVATED);
+ swipeAndHold();
+ } break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -859,6 +956,10 @@
case STATE_SCALING_AND_PANNING: {
returnToNormalFrom(STATE_PANNING);
} break;
+ case STATE_SINGLE_PANNING: {
+ send(upEvent());
+ returnToNormalFrom(STATE_ACTIVATED);
+ } break;
default: throw new IllegalArgumentException("Illegal state: " + state);
}
}
@@ -906,6 +1007,11 @@
send(moveEvent(DEFAULT_X * 2, DEFAULT_Y * 2));
}
+ private void swipeAndHold(PointF start, PointF end) {
+ send(downEvent(start.x, start.y));
+ send(moveEvent(end.x, end.y));
+ }
+
private void longTap() {
send(downEvent());
fastForward(2000);
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
new file mode 100644
index 0000000..0ffa891
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.contentcapture"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
new file mode 100644
index 0000000..419508c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.contentprotection"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index a109d5c..f552ab2 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -406,7 +406,6 @@
UriGrantsManagerInternal mUgmInternal;
@Mock
AppOpsManager mAppOpsManager;
- private AppOpsManager.OnOpChangedListener mOnPermissionChangeListener;
@Mock
private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback
mNotificationAssistantAccessGrantedCallback;
@@ -606,12 +605,6 @@
tr.addOverride(com.android.internal.R.string.config_defaultSearchSelectorPackageName,
SEARCH_SELECTOR_PKG);
- doAnswer(invocation -> {
- mOnPermissionChangeListener = invocation.getArgument(2);
- return null;
- }).when(mAppOpsManager).startWatchingMode(eq(AppOpsManager.OP_POST_NOTIFICATION), any(),
- any());
-
mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
@@ -3224,6 +3217,48 @@
}
@Test
+ public void testUpdateAppNotifyCreatorBlock() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
+ Thread.sleep(500);
+ waitForIdle();
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+ assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+ captor.getValue().getAction());
+ assertEquals(PKG, captor.getValue().getPackage());
+ assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ }
+
+ @Test
+ public void testUpdateAppNotifyCreatorBlock_notIfMatchesExistingSetting() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, 0, false);
+ verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null));
+ }
+
+ @Test
+ public void testUpdateAppNotifyCreatorUnblock() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true);
+ Thread.sleep(500);
+ waitForIdle();
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+ assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+ captor.getValue().getAction());
+ assertEquals(PKG, captor.getValue().getPackage());
+ assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ }
+
+ @Test
public void testUpdateChannelNotifyCreatorBlock() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
@@ -12139,134 +12174,6 @@
any(), eq(FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER | FLAG_SERVICE_SENDER));
}
- @Test
- public void onOpChanged_permissionRevoked_cancelsAllNotificationsFromPackage()
- throws RemoteException {
- // Have preexisting posted notifications from revoked package and other packages.
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("revoked", 1001, 1, 0), mTestNotificationChannel));
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
- // Have preexisting enqueued notifications from revoked package and other packages.
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("revoked", 1001, 3, 0), mTestNotificationChannel));
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
- when(mPackageManagerInternal.getPackageUid("revoked", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "revoked", 0);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(1);
- assertThat(mService.mNotificationList.get(0).getSbn().getPackageName()).isEqualTo("other");
- assertThat(mService.mEnqueuedNotifications).hasSize(1);
- assertThat(mService.mEnqueuedNotifications.get(0).getSbn().getPackageName()).isEqualTo(
- "other");
- }
-
- @Test
- public void onOpChanged_permissionStillGranted_notificationsAreNotAffected()
- throws RemoteException {
- // NOTE: This combination (receiving the onOpChanged broadcast for a package, the permission
- // being now granted, AND having previously posted notifications from said package) should
- // never happen (if we trust the broadcasts are correct). So this test is for a what-if
- // scenario, to verify we still handle it reasonably.
-
- // Have preexisting posted notifications from specific package and other packages.
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("granted", 1001, 1, 0), mTestNotificationChannel));
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
- // Have preexisting enqueued notifications from specific package and other packages.
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("granted", 1001, 3, 0), mTestNotificationChannel));
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
- when(mPackageManagerInternal.getPackageUid("granted", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "granted", 0);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
- }
-
- @Test
- public void onOpChanged_permissionGranted_notifiesAppUnblocked() throws Exception {
- when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
- waitForIdle();
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
-
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
- assertThat(captor.getValue().getAction()).isEqualTo(
- NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
- assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
- assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)).isFalse();
- }
-
- @Test
- public void onOpChanged_permissionRevoked_notifiesAppBlocked() throws Exception {
- when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
- waitForIdle();
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
-
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
- assertThat(captor.getValue().getAction()).isEqualTo(
- NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
- assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
- assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false)).isTrue();
- }
-
- @Test
- public void setNotificationsEnabledForPackage_disabling_clearsNotifications() throws Exception {
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("package", 1001, 1, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(1);
- when(mPackageManagerInternal.getPackageUid("package", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasRequestedPermission(any(), eq("package"), anyInt())).thenReturn(
- true);
-
- // Start with granted permission and simulate effect of revoking it.
- when(mPermissionHelper.hasPermission(1001)).thenReturn(true);
- doAnswer(invocation -> {
- when(mPermissionHelper.hasPermission(1001)).thenReturn(false);
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "package", 0);
- return null;
- }).when(mPermissionHelper).setNotificationPermission("package", 0, false, true);
-
- mBinderService.setNotificationsEnabledForPackage("package", 1001, false);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(0);
-
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
- verify(mContext).sendBroadcastAsUser(any(), eq(UserHandle.of(0)), eq(null));
- }
-
private static <T extends Parcelable> T parcelAndUnparcel(T source,
Parcelable.Creator<T> creator) {
Parcel parcel = Parcel.obtain();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index dedb8f1..3ee75de 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -771,7 +771,7 @@
mZenModeHelper.mConfig = null; // will evaluate config to zen mode off
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -798,7 +798,7 @@
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -825,7 +825,7 @@
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -2269,7 +2269,7 @@
// Artificially turn zen mode "on". Re-evaluating zen mode should cause it to turn back off
// given that we don't have any zen rules active.
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
// Check that the change actually took: zen mode should be off now
assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index abf21a5..7eab06a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -656,7 +656,7 @@
topSplitPrimary.getVisibility(null /* starting */));
// Make primary split root transient-hide.
spyOn(splitPrimary.mTransitionController);
- doReturn(true).when(splitPrimary.mTransitionController).isTransientHide(
+ doReturn(true).when(splitPrimary.mTransitionController).isTransientVisible(
organizer.mPrimary);
// The split root and its top become visible.
assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index ffecafb..5154d17 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1488,6 +1488,47 @@
}
@Test
+ public void testIsTransientVisible() {
+ final ActivityRecord appB = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setVisible(false).build();
+ final ActivityRecord recent = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setVisible(false).build();
+ final ActivityRecord appA = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task taskA = appA.getTask();
+ final Task taskB = appB.getTask();
+ final Task taskRecent = recent.getTask();
+ registerTestTransitionPlayer();
+ final TransitionController controller = mRootWindowContainer.mTransitionController;
+ final Transition transition = createTestTransition(TRANSIT_OPEN, controller);
+ controller.moveToCollecting(transition);
+ transition.collect(recent);
+ transition.collect(taskA);
+ transition.setTransientLaunch(recent, taskA);
+ taskRecent.moveToFront("move-recent-to-front");
+
+ // During collecting and playing, the recent is on top so it is visible naturally.
+ // While B needs isTransientVisible to keep visibility because it is occluded by recents.
+ assertFalse(controller.isTransientVisible(taskB));
+ assertTrue(controller.isTransientVisible(taskA));
+ assertFalse(controller.isTransientVisible(taskRecent));
+ // Switch to playing state.
+ transition.onTransactionReady(transition.getSyncId(), mMockT);
+ assertTrue(controller.isTransientVisible(taskA));
+
+ // Switch to another task. For example, use gesture navigation to switch tasks.
+ taskB.moveToFront("move-b-to-front");
+ // The previous app (taskA) should be paused first so it loses transient visible. Because
+ // visually it is taskA -> taskB, the pause -> resume order should be the same.
+ assertFalse(controller.isTransientVisible(taskA));
+ // Keep the recent visible so there won't be 2 activities pausing at the same time. It is
+ // to avoid the latency to resume the current top, i.e. appB.
+ assertTrue(controller.isTransientVisible(taskRecent));
+ // The recent is paused after the transient transition is finished.
+ controller.finishTransition(transition);
+ assertFalse(controller.isTransientVisible(taskRecent));
+ }
+
+ @Test
public void testNotReadyPushPop() {
final TransitionController controller = new TestTransitionController(mAtm);
controller.setSyncEngine(mWm.mSyncEngine);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 7a16060..94b090f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -200,7 +200,7 @@
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
- @FlakyTest(bugId = 251217585)
+ @FlakyTest(bugId = 285980483)
@Test
override fun focusChanges() {
super.focusChanges()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 4164c0d..df9780e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -21,6 +21,7 @@
import android.content.res.Resources
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.ComponentNameMatcher.Companion.SPLASH_SCREEN
import android.tools.common.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
@@ -190,6 +191,16 @@
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(launchNewTaskApp)
+ )
+ }
+ }
+
companion object {
private fun getWallpaperPackage(instrumentation: Instrumentation): IComponentMatcher {
val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext)