summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/parsing/OWNERS1
-rw-r--r--core/java/android/gesture/OWNERS1
-rw-r--r--core/java/android/metrics/OWNERS1
-rw-r--r--core/java/android/os/PerfettoTrackEventExtra.java5
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java7
-rw-r--r--core/java/android/view/OWNERS1
-rw-r--r--core/java/android/view/ViewRootImpl.java50
-rw-r--r--core/java/android/view/inspector/OWNERS1
-rw-r--r--core/java/android/window/TransitionInfo.java4
-rw-r--r--core/java/com/android/internal/config/appcloning/OWNERS1
-rw-r--r--core/java/com/android/internal/widget/remotecompose/OWNERS1
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/layout/accessibility_autoclick_type_panel.xml4
-rw-r--r--core/tests/coretests/src/android/os/PerfettoTraceTest.java2
-rw-r--r--data/etc/privapp-permissions-platform.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt171
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt19
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt176
-rw-r--r--libs/hwui/CanvasTransform.cpp44
-rw-r--r--libs/hwui/RenderNode.cpp33
-rw-r--r--libs/hwui/hwui/Bitmap.cpp13
-rw-r--r--libs/hwui/hwui/Bitmap.h1
-rw-r--r--libs/hwui/jni/GIFMovie.cpp10
-rw-r--r--location/java/android/location/LocationRequest.java9
-rw-r--r--location/java/android/location/flags/location.aconfig11
-rw-r--r--packages/Shell/OWNERS1
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt17
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt33
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java76
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt115
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt44
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelTest.kt97
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt50
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt20
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java19
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/ScrimController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt4
-rw-r--r--proto/src/metrics_constants/OWNERS1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java29
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickLinearLayout.java80
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java18
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsRegistry.java2
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java113
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java1
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java93
-rw-r--r--services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java10
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java16
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java29
-rw-r--r--services/core/java/com/android/server/wm/Transition.java5
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java13
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java25
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java (renamed from services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java)7
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java89
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickLinearLayoutTest.java102
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java18
-rw-r--r--tools/codegen/OWNERS1
81 files changed, 1662 insertions, 426 deletions
diff --git a/core/java/android/content/pm/parsing/OWNERS b/core/java/android/content/pm/parsing/OWNERS
index 445a8330037b..b8fa1a93ed78 100644
--- a/core/java/android/content/pm/parsing/OWNERS
+++ b/core/java/android/content/pm/parsing/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 36137
-chiuwinson@google.com
patb@google.com
diff --git a/core/java/android/gesture/OWNERS b/core/java/android/gesture/OWNERS
index 168630af6da4..ffa753aa65b2 100644
--- a/core/java/android/gesture/OWNERS
+++ b/core/java/android/gesture/OWNERS
@@ -3,5 +3,4 @@
romainguy@google.com
adamp@google.com
aurimas@google.com
-nduca@google.com
sumir@google.com
diff --git a/core/java/android/metrics/OWNERS b/core/java/android/metrics/OWNERS
index ba867e0cad2b..98aaf3f47b21 100644
--- a/core/java/android/metrics/OWNERS
+++ b/core/java/android/metrics/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 26805
cwren@android.com
-cwren@google.com
diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java
index 2848bcb8ad34..8a3a5be9c934 100644
--- a/core/java/android/os/PerfettoTrackEventExtra.java
+++ b/core/java/android/os/PerfettoTrackEventExtra.java
@@ -214,9 +214,6 @@ public final class PerfettoTrackEventExtra {
* Initialize the builder for a new trace event.
*/
public Builder init(int traceType, PerfettoTrace.Category category) {
- if (!category.isEnabled()) {
- return this;
- }
mTraceType = traceType;
mCategory = category;
mEventName = "";
@@ -228,7 +225,7 @@ public final class PerfettoTrackEventExtra {
mExtra.reset();
// Reset after on init in case the thread created builders without calling emit
- return initInternal(this, null, true);
+ return initInternal(this, null, category.isEnabled());
}
/**
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 41a64e22e058..744cdf6629e7 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1323,14 +1323,13 @@ public abstract class WallpaperService extends Service {
redrawNeeded ? 1 : 0));
return;
}
-
- final int transformHint = SurfaceControl.rotationToBufferTransform(
- (mDisplay.getInstallOrientation() + mDisplay.getRotation()) % 4);
- mSurfaceControl.setTransformHint(transformHint);
WindowLayout.computeSurfaceSize(mLayout, maxBounds, mWidth, mHeight,
mWinFrames.frame, false /* dragResizing */, mSurfaceSize);
if (mSurfaceControl.isValid()) {
+ final int transformHint = SurfaceControl.rotationToBufferTransform(
+ (mDisplay.getInstallOrientation() + mDisplay.getRotation()) % 4);
+ mSurfaceControl.setTransformHint(transformHint);
if (mBbqSurfaceControl == null) {
mBbqSurfaceControl = new SurfaceControl.Builder()
.setName("Wallpaper BBQ wrapper")
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 80484a6328e0..3353923292e1 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -3,7 +3,6 @@
romainguy@google.com
adamp@google.com
aurimas@google.com
-nduca@google.com
sumir@google.com
ogunwale@google.com
jjaggi@google.com
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e157da72196a..9d0773f0a606 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2071,26 +2071,40 @@ public final class ViewRootImpl implements ViewParent,
*/
@VisibleForTesting
public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() {
- if (forceInvertColor()) {
- // Force invert ignores all developer opt-outs.
- // We also ignore dark theme, since the app developer can override the user's preference
- // for dark mode in configuration.uiMode. Instead, we assume that both force invert and
- // the system's dark theme are enabled.
- if (getUiModeManager().getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK) {
- return ForceDarkType.FORCE_INVERT_COLOR_DARK;
- }
- }
-
- boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES;
- if (useAutoDark) {
- boolean forceDarkAllowedDefault =
- SystemProperties.getBoolean(ThreadedRenderer.DEBUG_FORCE_DARK, false);
- TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme);
- useAutoDark = a.getBoolean(R.styleable.Theme_isLightTheme, true)
- && a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault);
+ TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme);
+ try {
+ if (forceInvertColor()) {
+ // Force invert ignores all developer opt-outs.
+ // We also ignore dark theme, since the app developer can override the user's
+ // preference for dark mode in configuration.uiMode. Instead, we assume that both
+ // force invert and the system's dark theme are enabled.
+ if (getUiModeManager().getForceInvertState() ==
+ UiModeManager.FORCE_INVERT_TYPE_DARK) {
+ final boolean isLightTheme =
+ a.getBoolean(R.styleable.Theme_isLightTheme, false);
+ // TODO: b/372558459 - Also check the background ColorDrawable color lightness
+ // TODO: b/368725782 - Use hwui color area detection instead of / in
+ // addition to these heuristics.
+ if (isLightTheme) {
+ return ForceDarkType.FORCE_INVERT_COLOR_DARK;
+ } else {
+ return ForceDarkType.NONE;
+ }
+ }
+ }
+
+ boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES;
+ if (useAutoDark) {
+ boolean forceDarkAllowedDefault =
+ SystemProperties.getBoolean(ThreadedRenderer.DEBUG_FORCE_DARK, false);
+ useAutoDark = a.getBoolean(R.styleable.Theme_isLightTheme, true)
+ && a.getBoolean(R.styleable.Theme_forceDarkAllowed,
+ forceDarkAllowedDefault);
+ }
+ return useAutoDark ? ForceDarkType.FORCE_DARK : ForceDarkType.NONE;
+ } finally {
a.recycle();
}
- return useAutoDark ? ForceDarkType.FORCE_DARK : ForceDarkType.NONE;
}
private void updateForceDarkMode() {
diff --git a/core/java/android/view/inspector/OWNERS b/core/java/android/view/inspector/OWNERS
index 705f4b342d42..f3450344ea81 100644
--- a/core/java/android/view/inspector/OWNERS
+++ b/core/java/android/view/inspector/OWNERS
@@ -2,5 +2,4 @@ romainguy@google.com
alanv@google.com
adamp@google.com
aurimas@google.com
-nduca@google.com
sumir@google.com
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 63c55ad6a889..be4edc3b6ec5 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -29,6 +29,7 @@ import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_AOD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_NONE;
@@ -375,7 +376,8 @@ public final class TransitionInfo implements Parcelable {
*/
public boolean hasChangesOrSideEffects() {
return !mChanges.isEmpty() || isKeyguardGoingAway()
- || (mFlags & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0;
+ || (mFlags & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0
+ || (mFlags & TRANSIT_FLAG_AOD_APPEARING) != 0;
}
/**
diff --git a/core/java/com/android/internal/config/appcloning/OWNERS b/core/java/com/android/internal/config/appcloning/OWNERS
index 0645a8c54414..9369438deb07 100644
--- a/core/java/com/android/internal/config/appcloning/OWNERS
+++ b/core/java/com/android/internal/config/appcloning/OWNERS
@@ -1,3 +1,2 @@
# Bug component: 1207885
jigarthakkar@google.com
-saumyap@google.com \ No newline at end of file
diff --git a/core/java/com/android/internal/widget/remotecompose/OWNERS b/core/java/com/android/internal/widget/remotecompose/OWNERS
index e163474bccb9..c606744df150 100644
--- a/core/java/com/android/internal/widget/remotecompose/OWNERS
+++ b/core/java/com/android/internal/widget/remotecompose/OWNERS
@@ -1,6 +1,5 @@
nicolasroard@google.com
hoford@google.com
-jnichol@google.com
sihua@google.com
sunnygoyal@google.com
oscarad@google.com
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ee6899cf866b..e16ce9849ff2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8995,13 +8995,13 @@
<!-- @SystemApi
@FlaggedApi("android.permission.flags.text_classifier_choice_api_enabled")
- This permission is required to access the specific text classifier you need from the
+ This permission is required to access the specific text classifier from the
TextClassificationManager.
- <p>Protection level: signature|role
+ <p>Protection level: signature|role|privileged
@hide
-->
<permission android:name="android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE"
- android:protectionLevel="signature|role"
+ android:protectionLevel="signature|role|privileged"
android:featureFlag="android.permission.flags.text_classifier_choice_api_enabled"/>
<!-- Attribution for Geofencing service. -->
diff --git a/core/res/res/layout/accessibility_autoclick_type_panel.xml b/core/res/res/layout/accessibility_autoclick_type_panel.xml
index cedbdc175488..902ef7fc38e8 100644
--- a/core/res/res/layout/accessibility_autoclick_type_panel.xml
+++ b/core/res/res/layout/accessibility_autoclick_type_panel.xml
@@ -17,7 +17,7 @@
*/
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.server.accessibility.autoclick.AutoclickLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/accessibility_autoclick_type_panel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -130,4 +130,4 @@
</LinearLayout>
-</LinearLayout>
+</com.android.server.accessibility.autoclick.AutoclickLinearLayout>
diff --git a/core/tests/coretests/src/android/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
index 69150150d6f9..790ac4a55dc6 100644
--- a/core/tests/coretests/src/android/os/PerfettoTraceTest.java
+++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
@@ -33,6 +33,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -611,6 +612,7 @@ public class PerfettoTraceTest {
@Test
@RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ @Ignore("b/303199244")
public void testMessageQueue() throws Exception {
TraceConfig traceConfig = getTraceConfig("mq");
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 15f70298198f..9234902335c1 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -619,6 +619,8 @@ applications that come with the platform
<permission name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"/>
<permission name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"/>
<permission name="android.permission.READ_COLOR_ZONES"/>
+ <!-- Permission required for CTS test - CtsTextClassifierTestCases -->
+ <permission name="android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
index b507ca2019a9..3f21e74a7d03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.app.ActivityManager;
+import android.window.DesktopExperienceFlags;
import android.window.DisplayAreaInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -67,7 +68,7 @@ public class PipDesktopState {
/** Returns whether PiP in Connected Displays is enabled by checking the flag. */
public boolean isConnectedDisplaysPipEnabled() {
- return Flags.enableConnectedDisplaysPip();
+ return DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue();
}
/** Returns whether the display with the PiP task is in freeform windowing mode. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
index 7074e8bc9cce..6c6d830e915e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
@@ -21,6 +21,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.os.Handler
import android.os.IBinder
import android.view.SurfaceControl.Transaction
+import android.view.WindowManager.TRANSIT_TO_BACK
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
@@ -61,7 +62,9 @@ class DesktopMinimizationTransitionHandler(
finishTransaction: Transaction,
finishCallback: Transitions.TransitionFinishCallback,
): Boolean {
- if (!TransitionUtil.isClosingType(info.type)) return false
+ val shouldAnimate =
+ TransitionUtil.isClosingType(info.type) || info.type == Transitions.TRANSIT_MINIMIZE
+ if (!shouldAnimate) return false
val animations = mutableListOf<Animator>()
val onAnimFinish: (Animator) -> Unit = { animator ->
@@ -75,10 +78,14 @@ class DesktopMinimizationTransitionHandler(
}
}
+ val checkChangeMode = { change: TransitionInfo.Change ->
+ change.mode == info.type ||
+ (info.type == Transitions.TRANSIT_MINIMIZE && change.mode == TRANSIT_TO_BACK)
+ }
animations +=
info.changes
.filter {
- it.mode == info.type && it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM
+ checkChangeMode(it) && it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM
}
.mapNotNull { createMinimizeAnimation(it, finishTransaction, onAnimFinish) }
if (animations.isEmpty()) return false
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 5de3be4bbfc0..8f7e52ea2108 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -677,11 +677,7 @@ class DesktopTasksController(
// Bring other apps to front first.
bringDesktopAppsToFrontBeforeShowingNewTask(displayId, wct, task.taskId)
}
- if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
- prepareMoveTaskToDesk(wct, task, deskId)
- } else {
- addMoveToDesktopChanges(wct, task)
- }
+ addMoveToDeskTaskChanges(wct = wct, task = task, deskId = deskId)
return taskIdToMinimize
}
@@ -1260,6 +1256,8 @@ class DesktopTasksController(
* Move [task] to display with [displayId].
*
* No-op if task is already on that display per [RunningTaskInfo.displayId].
+ *
+ * TODO: b/399411604 - split this up into smaller functions.
*/
private fun moveToDisplay(task: RunningTaskInfo, displayId: Int) {
logV("moveToDisplay: taskId=%d displayId=%d", task.taskId, displayId)
@@ -1315,16 +1313,20 @@ class DesktopTasksController(
// TODO: b/393977830 and b/397437641 - do not assume that freeform==desktop.
if (!task.isFreeform) {
- addMoveToDesktopChanges(wct, task, displayId)
- } else if (Flags.enableMoveToNextDisplayShortcut()) {
- applyFreeformDisplayChange(wct, task, displayId)
+ addMoveToDeskTaskChanges(wct = wct, task = task, deskId = destinationDeskId)
+ } else {
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ desksOrganizer.moveTaskToDesk(wct, destinationDeskId, task)
+ }
+ if (Flags.enableMoveToNextDisplayShortcut()) {
+ applyFreeformDisplayChange(wct, task, displayId)
+ }
}
- if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
- desksOrganizer.moveTaskToDesk(wct, destinationDeskId, task)
- } else {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
wct.reparent(task.token, displayAreaInfo.token, /* onTop= */ true)
}
+
addDeskActivationChanges(destinationDeskId, wct)
val activationRunnable: RunOnTransitStart = { transition ->
desksTransitionObserver.addPendingTransition(
@@ -2062,12 +2064,13 @@ class DesktopTasksController(
triggerTask?.let { task ->
when {
// Check if freeform task launch during recents should be handled
- shouldHandleMidRecentsFreeformLaunch -> handleMidRecentsFreeformTaskLaunch(task)
+ shouldHandleMidRecentsFreeformLaunch ->
+ handleMidRecentsFreeformTaskLaunch(task, transition)
// Check if the closing task needs to be handled
TransitionUtil.isClosingType(request.type) ->
handleTaskClosing(task, transition, request.type)
// Check if the top task shouldn't be allowed to enter desktop mode
- isIncompatibleTask(task) -> handleIncompatibleTaskLaunch(task)
+ isIncompatibleTask(task) -> handleIncompatibleTaskLaunch(task, transition)
// Check if fullscreen task should be updated
task.isFullscreen -> handleFullscreenTaskLaunch(task, transition)
// Check if freeform task should be updated
@@ -2306,20 +2309,23 @@ class DesktopTasksController(
* This is a special case where we want to launch the task in fullscreen instead of freeform.
*/
private fun handleMidRecentsFreeformTaskLaunch(
- task: RunningTaskInfo
+ task: RunningTaskInfo,
+ transition: IBinder,
): WindowContainerTransaction? {
logV("DesktopTasksController: handleMidRecentsFreeformTaskLaunch")
val wct = WindowContainerTransaction()
- addMoveToFullscreenChanges(
- wct = wct,
- taskInfo = task,
- willExitDesktop =
- willExitDesktop(
- triggerTaskId = task.taskId,
- displayId = task.displayId,
- forceExitDesktop = true,
- ),
- )
+ val runOnTransitStart =
+ addMoveToFullscreenChanges(
+ wct = wct,
+ taskInfo = task,
+ willExitDesktop =
+ willExitDesktop(
+ triggerTaskId = task.taskId,
+ displayId = task.displayId,
+ forceExitDesktop = true,
+ ),
+ )
+ runOnTransitStart?.invoke(transition)
wct.reorder(task.token, true)
return wct
}
@@ -2343,16 +2349,18 @@ class DesktopTasksController(
// launched. We should make this task go to fullscreen instead of freeform. Note
// that this means any re-launch of a freeform window outside of desktop will be in
// fullscreen as long as default-desktop flag is disabled.
- addMoveToFullscreenChanges(
- wct = wct,
- taskInfo = task,
- willExitDesktop =
- willExitDesktop(
- triggerTaskId = task.taskId,
- displayId = task.displayId,
- forceExitDesktop = true,
- ),
- )
+ val runOnTransitStart =
+ addMoveToFullscreenChanges(
+ wct = wct,
+ taskInfo = task,
+ willExitDesktop =
+ willExitDesktop(
+ triggerTaskId = task.taskId,
+ displayId = task.displayId,
+ forceExitDesktop = true,
+ ),
+ )
+ runOnTransitStart?.invoke(transition)
return wct
}
bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
@@ -2416,7 +2424,8 @@ class DesktopTasksController(
if (shouldFullscreenTaskLaunchSwitchToDesktop(task)) {
logD("Switch fullscreen task to freeform on transition: taskId=%d", task.taskId)
return WindowContainerTransaction().also { wct ->
- addMoveToDesktopChanges(wct, task)
+ val deskId = getDefaultDeskId(task.displayId)
+ addMoveToDeskTaskChanges(wct = wct, task = task, deskId = deskId)
// In some launches home task is moved behind new task being launched. Make sure
// that's not the case for launches in desktop. Also, if this launch is the first
// one to trigger the desktop mode (e.g., when [forceEnterDesktop()]), activate the
@@ -2447,7 +2456,8 @@ class DesktopTasksController(
// If a freeform task receives a request for a fullscreen launch, apply the same
// changes we do for similar transitions. The task not having WINDOWING_MODE_UNDEFINED
// set when needed can interfere with future split / multi-instance transitions.
- return WindowContainerTransaction().also { wct ->
+ val wct = WindowContainerTransaction()
+ val runOnTransitStart =
addMoveToFullscreenChanges(
wct = wct,
taskInfo = task,
@@ -2458,7 +2468,8 @@ class DesktopTasksController(
forceExitDesktop = true,
),
)
- }
+ runOnTransitStart?.invoke(transition)
+ return wct
}
return null
}
@@ -2473,7 +2484,10 @@ class DesktopTasksController(
* If a task is not compatible with desktop mode freeform, it should always be launched in
* fullscreen.
*/
- private fun handleIncompatibleTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+ private fun handleIncompatibleTaskLaunch(
+ task: RunningTaskInfo,
+ transition: IBinder,
+ ): WindowContainerTransaction? {
logV("handleIncompatibleTaskLaunch")
if (!isDesktopModeShowing(task.displayId) && !forceEnterDesktop(task.displayId)) return null
// Only update task repository for transparent task.
@@ -2485,7 +2499,8 @@ class DesktopTasksController(
}
// Already fullscreen, no-op.
if (task.isFullscreen) return null
- return WindowContainerTransaction().also { wct ->
+ val wct = WindowContainerTransaction()
+ val runOnTransitStart =
addMoveToFullscreenChanges(
wct = wct,
taskInfo = task,
@@ -2496,7 +2511,8 @@ class DesktopTasksController(
forceExitDesktop = true,
),
)
- }
+ runOnTransitStart?.invoke(transition)
+ return wct
}
/**
@@ -2543,55 +2559,44 @@ class DesktopTasksController(
}
/**
- * Apply all changes required when task is first added to desktop. Uses the task's current
- * display by default to apply initial bounds and placement relative to the display. Use a
- * different [displayId] if the task should be moved to a different display.
+ * Applies the [wct] changes needed when a task is first moving to a desk.
+ *
+ * Note that this recalculates the initial bounds of the task, so it should not be used when
+ * transferring a task between desks.
+ *
+ * TODO: b/362720497 - this should be improved to be reusable by desk-to-desk CUJs where
+ * [DesksOrganizer.moveTaskToDesk] needs to be called and even cross-display CUJs where
+ * [applyFreeformDisplayChange] needs to be called. Potentially by comparing source vs
+ * destination desk ids and display ids, or adding extra arguments to the function.
*/
- @VisibleForTesting
- @Deprecated("Deprecated with multiple desks", ReplaceWith("prepareMoveTaskToDesk()"))
- fun addMoveToDesktopChanges(
+ fun addMoveToDeskTaskChanges(
wct: WindowContainerTransaction,
- taskInfo: RunningTaskInfo,
- displayId: Int = taskInfo.displayId,
- ) {
- val displayLayout = displayController.getDisplayLayout(displayId) ?: return
- val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)!!
- val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
- // TODO: b/397437641 - reconsider the windowing mode choice when multiple desks is enabled.
- val targetWindowingMode =
- if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) {
- // Display windowing is freeform, set to undefined and inherit it
- WINDOWING_MODE_UNDEFINED
- } else {
- WINDOWING_MODE_FREEFORM
- }
- val initialBounds = getInitialBounds(displayLayout, taskInfo, displayId)
-
- if (canChangeTaskPosition(taskInfo)) {
- wct.setBounds(taskInfo.token, initialBounds)
- }
- wct.setWindowingMode(taskInfo.token, targetWindowingMode)
- wct.reorder(taskInfo.token, /* onTop= */ true)
- if (useDesktopOverrideDensity()) {
- wct.setDensityDpi(taskInfo.token, DESKTOP_DENSITY_OVERRIDE)
- }
- }
-
- private fun prepareMoveTaskToDesk(
- wct: WindowContainerTransaction,
- taskInfo: RunningTaskInfo,
+ task: RunningTaskInfo,
deskId: Int,
) {
- if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
- val displayId = taskRepository.getDisplayForDesk(deskId)
- val displayLayout = displayController.getDisplayLayout(displayId) ?: return
- val initialBounds = getInitialBounds(displayLayout, taskInfo, displayId)
- if (canChangeTaskPosition(taskInfo)) {
- wct.setBounds(taskInfo.token, initialBounds)
+ val targetDisplayId = taskRepository.getDisplayForDesk(deskId)
+ val displayLayout = displayController.getDisplayLayout(targetDisplayId) ?: return
+ val initialBounds = getInitialBounds(displayLayout, task, targetDisplayId)
+ if (canChangeTaskPosition(task)) {
+ wct.setBounds(task.token, initialBounds)
+ }
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ desksOrganizer.moveTaskToDesk(wct = wct, deskId = deskId, task = task)
+ } else {
+ val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(targetDisplayId)!!
+ val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
+ val targetWindowingMode =
+ if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) {
+ // Display windowing is freeform, set to undefined and inherit it
+ WINDOWING_MODE_UNDEFINED
+ } else {
+ WINDOWING_MODE_FREEFORM
+ }
+ wct.setWindowingMode(task.token, targetWindowingMode)
+ wct.reorder(task.token, /* onTop= */ true)
}
- desksOrganizer.moveTaskToDesk(wct, deskId = deskId, task = taskInfo)
if (useDesktopOverrideDensity()) {
- wct.setDensityDpi(taskInfo.token, DESKTOP_DENSITY_OVERRIDE)
+ wct.setDensityDpi(task.token, DESKTOP_DENSITY_OVERRIDE)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index d666126b91ba..c0a0f469add4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
+import static android.view.WindowManager.TRANSIT_FLAG_AOD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
@@ -200,7 +201,8 @@ public class KeyguardTransitionHandler
transition, info, startTransaction, finishTransaction, finishCallback);
}
- if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0) {
+ if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0
+ || (info.getFlags() & TRANSIT_FLAG_AOD_APPEARING) != 0) {
return startAnimation(mAppearTransition, "appearing",
transition, info, startTransaction, finishTransaction, finishCallback);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index d16c5782177e..6012fe66188d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -32,6 +32,7 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.SurfaceControl;
+import android.window.DesktopExperienceFlags;
import android.window.DisplayAreaInfo;
import android.window.WindowContainerTransaction;
@@ -41,7 +42,6 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.Preconditions;
-import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayChangeController;
@@ -303,7 +303,8 @@ public class PipController implements ConfigurationChangeListener,
public void onDisplayRemoved(int displayId) {
// If PiP was active on an external display that is removed, clean up states and set
// {@link PipDisplayLayoutState} to DEFAULT_DISPLAY.
- if (Flags.enableConnectedDisplaysPip() && mPipTransitionState.isInPip()
+ if (DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue()
+ && mPipTransitionState.isInPip()
&& displayId == mPipDisplayLayoutState.getDisplayId()
&& displayId != DEFAULT_DISPLAY) {
mPipTransitionState.setState(PipTransitionState.EXITING_PIP);
@@ -385,7 +386,7 @@ public class PipController implements ConfigurationChangeListener,
// If PiP is enabled on Connected Displays, update PipDisplayLayoutState to have the correct
// display info that PiP is entering in.
- if (Flags.enableConnectedDisplaysPip()) {
+ if (DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue()) {
final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId);
if (displayLayout != null) {
mPipDisplayLayoutState.setDisplayId(displayId);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
index 0d1c57221fb9..3e6f688e6acc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
@@ -33,6 +33,7 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.transition.Transitions
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
@@ -154,6 +155,24 @@ class DesktopMinimizationTransitionHandlerTest : ShellTestCase() {
assertTrue("Should animate going to back freeform task close transition", animates)
}
+ @Test
+ fun startAnimation_minimizeTransitionToBackFreeformTask_returnsTrue() {
+ val animates =
+ handler.startAnimation(
+ transition = mock(),
+ info =
+ createTransitionInfo(
+ type = Transitions.TRANSIT_MINIMIZE,
+ task = createTask(WINDOWING_MODE_FREEFORM),
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {},
+ )
+
+ assertTrue("Should animate going to back freeform task minimize transition", animates)
+ }
+
private fun createTransitionInfo(
type: Int = WindowManager.TRANSIT_TO_BACK,
changeMode: Int = WindowManager.TRANSIT_TO_BACK,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index b0785df3542e..63bf6841dba4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -1114,44 +1114,44 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun addMoveToDesktopChanges_gravityLeft_noBoundsApplied() {
+ fun addMoveToDeskTaskChanges_gravityLeft_noBoundsApplied() {
setUpLandscapeDisplay()
val task = setUpFullscreenTask(gravity = Gravity.LEFT)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(finalBounds).isEqualTo(Rect())
}
@Test
- fun addMoveToDesktopChanges_gravityRight_noBoundsApplied() {
+ fun addMoveToDeskTaskChanges_gravityRight_noBoundsApplied() {
setUpLandscapeDisplay()
val task = setUpFullscreenTask(gravity = Gravity.RIGHT)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(finalBounds).isEqualTo(Rect())
}
@Test
- fun addMoveToDesktopChanges_gravityTop_noBoundsApplied() {
+ fun addMoveToDeskTaskChanges_gravityTop_noBoundsApplied() {
setUpLandscapeDisplay()
val task = setUpFullscreenTask(gravity = Gravity.TOP)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(finalBounds).isEqualTo(Rect())
}
@Test
- fun addMoveToDesktopChanges_gravityBottom_noBoundsApplied() {
+ fun addMoveToDeskTaskChanges_gravityBottom_noBoundsApplied() {
setUpLandscapeDisplay()
val task = setUpFullscreenTask(gravity = Gravity.BOTTOM)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(finalBounds).isEqualTo(Rect())
@@ -1192,7 +1192,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_positionBottomRight() {
+ fun addMoveToDeskTaskChanges_positionBottomRight() {
setUpLandscapeDisplay()
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
@@ -1201,7 +1201,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task = setUpFullscreenTask()
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
@@ -1210,7 +1210,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_positionTopLeft() {
+ fun addMoveToDeskTaskChanges_positionTopLeft() {
setUpLandscapeDisplay()
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
@@ -1219,7 +1219,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task = setUpFullscreenTask()
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
@@ -1228,7 +1228,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_positionBottomLeft() {
+ fun addMoveToDeskTaskChanges_positionBottomLeft() {
setUpLandscapeDisplay()
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
@@ -1237,7 +1237,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task = setUpFullscreenTask()
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
@@ -1246,7 +1246,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_positionTopRight() {
+ fun addMoveToDeskTaskChanges_positionTopRight() {
setUpLandscapeDisplay()
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
@@ -1255,7 +1255,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task = setUpFullscreenTask()
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
@@ -1264,7 +1264,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_positionResetsToCenter() {
+ fun addMoveToDeskTaskChanges_positionResetsToCenter() {
setUpLandscapeDisplay()
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
@@ -1273,7 +1273,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task = setUpFullscreenTask()
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
@@ -1282,7 +1282,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_lastWindowSnapLeft_positionResetsToCenter() {
+ fun addMoveToDeskTaskChanges_lastWindowSnapLeft_positionResetsToCenter() {
setUpLandscapeDisplay()
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
@@ -1294,7 +1294,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task = setUpFullscreenTask()
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
@@ -1303,7 +1303,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_lastWindowSnapRight_positionResetsToCenter() {
+ fun addMoveToDeskTaskChanges_lastWindowSnapRight_positionResetsToCenter() {
setUpLandscapeDisplay()
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
@@ -1321,7 +1321,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task = setUpFullscreenTask()
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
@@ -1330,7 +1330,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_lastWindowMaximised_positionResetsToCenter() {
+ fun addMoveToDeskTaskChanges_lastWindowMaximised_positionResetsToCenter() {
setUpLandscapeDisplay()
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
@@ -1340,7 +1340,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task = setUpFullscreenTask()
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
@@ -1349,7 +1349,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
- fun addMoveToDesktopChanges_defaultToCenterIfFree() {
+ fun addMoveToDeskTaskChanges_defaultToCenterIfFree() {
setUpLandscapeDisplay()
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
@@ -1367,7 +1367,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task = setUpFullscreenTask()
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
@@ -1375,7 +1375,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun addMoveToDesktopChanges_excludeCaptionFromAppBounds_nonResizableLandscape() {
+ fun addMoveToDeskTaskChanges_excludeCaptionFromAppBounds_nonResizableLandscape() {
setUpLandscapeDisplay()
val task =
setUpFullscreenTask(
@@ -1385,7 +1385,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
whenever(desktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(task)).thenReturn(true)
val initialAspectRatio = calculateAspectRatio(task)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
val captionInsets = getAppHeaderHeight(context)
@@ -1397,7 +1397,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun addMoveToDesktopChanges_excludeCaptionFromAppBounds_nonResizablePortrait() {
+ fun addMoveToDeskTaskChanges_excludeCaptionFromAppBounds_nonResizablePortrait() {
setUpLandscapeDisplay()
val task =
setUpFullscreenTask(
@@ -1407,7 +1407,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
whenever(desktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(task)).thenReturn(true)
val initialAspectRatio = calculateAspectRatio(task)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
val captionInsets = getAppHeaderHeight(context)
@@ -1445,29 +1445,29 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_landscapeDevice_userFullscreenOverride_defaultPortraitBounds() {
+ fun addMoveToDeskTaskChanges_landscapeDevice_userFullscreenOverride_defaultPortraitBounds() {
setUpLandscapeDisplay()
val task = setUpFullscreenTask(enableUserFullscreenOverride = true)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_landscapeDevice_systemFullscreenOverride_defaultPortraitBounds() {
+ fun addMoveToDeskTaskChanges_landscapeDevice_systemFullscreenOverride_defaultPortraitBounds() {
setUpLandscapeDisplay()
val task = setUpFullscreenTask(enableSystemFullscreenOverride = true)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_landscapeDevice_portraitResizableApp_aspectRatioOverridden() {
+ fun addMoveToDeskTaskChanges_landscapeDevice_portraitResizableApp_aspectRatioOverridden() {
setUpLandscapeDisplay()
val task =
setUpFullscreenTask(
@@ -1476,36 +1476,36 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
aspectRatioOverrideApplied = true,
)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_portraitDevice_userFullscreenOverride_defaultPortraitBounds() {
+ fun addMoveToDeskTaskChanges_portraitDevice_userFullscreenOverride_defaultPortraitBounds() {
setUpPortraitDisplay()
val task = setUpFullscreenTask(enableUserFullscreenOverride = true)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_portraitDevice_systemFullscreenOverride_defaultPortraitBounds() {
+ fun addMoveToDeskTaskChanges_portraitDevice_systemFullscreenOverride_defaultPortraitBounds() {
setUpPortraitDisplay()
val task = setUpFullscreenTask(enableSystemFullscreenOverride = true)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun addMoveToDesktopChanges_portraitDevice_landscapeResizableApp_aspectRatioOverridden() {
+ fun addMoveToDeskTaskChanges_portraitDevice_landscapeResizableApp_aspectRatioOverridden() {
setUpPortraitDisplay()
val task =
setUpFullscreenTask(
@@ -1515,7 +1515,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
aspectRatioOverrideApplied = true,
)
val wct = WindowContainerTransaction()
- controller.addMoveToDesktopChanges(wct, task)
+ controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
}
@@ -2824,7 +2824,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun moveToNextDisplay_toDesktopInOtherDisplay_bringsExistingTasksToFront() {
val transition = Binder()
val sourceDeskId = 0
@@ -2856,7 +2855,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
)
- @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun moveToNextDisplay_toDesktopInOtherDisplay_movesHomeAndWallpaperToFront() {
val homeTask = setUpHomeTask(displayId = SECOND_DISPLAY)
whenever(desktopWallpaperActivityTokenProvider.getToken(SECOND_DISPLAY))
@@ -3506,6 +3504,39 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTask_switchToDesktop_movesTaskToDesk() {
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = 5)
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 5)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 5)
+
+ val fullscreenTask = createFullscreenTask()
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertNotNull(wct, "should handle request")
+ verify(desksOrganizer).moveTaskToDesk(wct = wct, deskId = 5, task = fullscreenTask)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskThatWasInactiveInDesk_tracksDeskDeactivation() {
+ // Set up and existing desktop task in an active desk.
+ val inactiveInDeskTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+ taskRepository.setDeskInactive(deskId = 0)
+
+ // Now the task is launching as fullscreen.
+ inactiveInDeskTask.configuration.windowConfiguration.windowingMode =
+ WINDOWING_MODE_FULLSCREEN
+ val transition = Binder()
+ val wct = controller.handleRequest(transition, createTransition(inactiveInDeskTask))
+
+ // Desk is deactivated.
+ assertNotNull(wct, "should handle request")
+ verify(desksTransitionsObserver)
+ .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId = 0))
+ }
+
+ @Test
fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
val homeTask = setUpHomeTask()
val freeformTask = setUpFreeformTask()
@@ -3681,6 +3712,20 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_freeformTaskFromInactiveDesk_tracksDeskDeactivation() {
+ val deskId = 0
+ val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ taskRepository.setDeskInactive(deskId = deskId)
+
+ val transition = Binder()
+ controller.handleRequest(transition, createTransition(freeformTask))
+
+ verify(desksTransitionsObserver)
+ .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId))
+ }
+
+ @Test
fun handleRequest_freeformTask_relaunchActiveTask_taskBecomesUndefined() {
val freeformTask = setUpFreeformTask()
markTaskHidden(freeformTask)
@@ -3928,6 +3973,24 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_recentsAnimationRunning_relaunchActiveTask_tracksDeskDeactivation() {
+ // Set up a visible freeform task
+ val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+ markTaskVisible(freeformTask)
+
+ // Mark recents animation running
+ recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)
+
+ val transition = Binder()
+ controller.handleRequest(transition, createTransition(freeformTask))
+
+ desksTransitionsObserver.addPendingTransition(
+ DeskTransition.DeactivateDesk(transition, deskId = 0)
+ )
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun handleRequest_topActivityTransparentWithoutDisplay_returnSwitchToFreeformWCT() {
val freeformTask = setUpFreeformTask()
@@ -4045,6 +4108,31 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ )
+ fun handleRequest_systemUIActivityWithDisplayInFreeformTask_inDesktop_tracksDeskDeactivation() {
+ val deskId = 5
+ taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ val systemUIPackageName =
+ context.resources.getString(com.android.internal.R.string.config_systemUi)
+ val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
+ val task =
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY).apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = false
+ }
+
+ val transition = Binder()
+ controller.handleRequest(transition, createTransition(task))
+
+ verify(desksTransitionsObserver)
+ .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId))
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun handleRequest_systemUIActivityWithoutDisplay_returnSwitchToFreeformWCT() {
val freeformTask = setUpFreeformTask()
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index 30e7a628f1f6..6f60d01e4395 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -55,12 +55,20 @@ SkColor makeDark(SkColor color) {
}
}
+SkColor invert(SkColor color) {
+ Lab lab = sRGBToLab(color);
+ lab.L = 100 - lab.L;
+ return LabToSRGB(lab, SkColorGetA(color));
+}
+
SkColor transformColor(ColorTransform transform, SkColor color) {
switch (transform) {
case ColorTransform::Light:
return makeLight(color);
case ColorTransform::Dark:
return makeDark(color);
+ case ColorTransform::Invert:
+ return invert(color);
default:
return color;
}
@@ -80,19 +88,6 @@ SkColor transformColorInverse(ColorTransform transform, SkColor color) {
static void applyColorTransform(ColorTransform transform, SkPaint& paint) {
if (transform == ColorTransform::None) return;
- if (transform == ColorTransform::Invert) {
- auto filter = SkHighContrastFilter::Make(
- {/* grayscale= */ false, SkHighContrastConfig::InvertStyle::kInvertLightness,
- /* contrast= */ 0.0f});
-
- if (paint.getColorFilter()) {
- paint.setColorFilter(SkColorFilters::Compose(filter, paint.refColorFilter()));
- } else {
- paint.setColorFilter(filter);
- }
- return;
- }
-
SkColor newColor = transformColor(transform, paint.getColor());
paint.setColor(newColor);
@@ -112,6 +107,22 @@ static void applyColorTransform(ColorTransform transform, SkPaint& paint) {
paint.setShader(SkGradientShader::MakeLinear(
info.fPoints, info.fColors, info.fColorOffsets, info.fColorCount,
info.fTileMode, info.fGradientFlags, nullptr));
+ } else {
+ if (transform == ColorTransform::Invert) {
+ // Since we're trying to invert every thing around this draw call, we invert
+ // the color of the draw call if we don't know what it is.
+ auto filter = SkHighContrastFilter::Make(
+ {/* grayscale= */ false,
+ SkHighContrastConfig::InvertStyle::kInvertLightness,
+ /* contrast= */ 0.0f});
+
+ if (paint.getColorFilter()) {
+ paint.setColorFilter(SkColorFilters::Compose(filter, paint.refColorFilter()));
+ } else {
+ paint.setColorFilter(filter);
+ }
+ return;
+ }
}
}
@@ -150,8 +161,13 @@ bool transformPaint(ColorTransform transform, SkPaint* paint) {
}
bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette) {
- palette = filterPalette(paint, palette);
bool shouldInvert = false;
+ if (transform == ColorTransform::Invert && palette != BitmapPalette::Colorful) {
+ // When the transform is Invert we invert any image that is not deemed "colorful",
+ // regardless of calculated image brightness.
+ shouldInvert = true;
+ }
+ palette = filterPalette(paint, palette);
if (palette == BitmapPalette::Light && transform == ColorTransform::Dark) {
shouldInvert = true;
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 4801bd1038a3..8b4e59aa73e2 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -27,6 +27,7 @@
#include "DamageAccumulator.h"
#include "Debug.h"
+#include "FeatureFlags.h"
#include "Properties.h"
#include "TreeInfo.h"
#include "VectorDrawable.h"
@@ -398,26 +399,32 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {
deleteDisplayList(observer, info);
mDisplayList = std::move(mStagingDisplayList);
if (mDisplayList) {
- WebViewSyncData syncData{.applyForceDark = shouldEnableForceDark(info)};
+ WebViewSyncData syncData{.applyForceDark = shouldEnableForceDark(info) ||
+ (info && isForceInvertDark(*info))};
mDisplayList.syncContents(syncData);
handleForceDark(info);
}
}
+// Return true if the tree should use the force invert feature that inverts
+// the entire tree to darken it.
inline bool RenderNode::isForceInvertDark(TreeInfo& info) {
- return CC_UNLIKELY(
- info.forceDarkType == android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK);
+ return CC_UNLIKELY(info.forceDarkType ==
+ android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK);
}
+// Return true if the tree should use the force dark feature that selectively
+// darkens light nodes on the tree.
inline bool RenderNode::shouldEnableForceDark(TreeInfo* info) {
- return CC_UNLIKELY(
- info &&
- (!info->disableForceDark || isForceInvertDark(*info)));
+ return CC_UNLIKELY(info && !info->disableForceDark);
}
-
-
-void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
+void RenderNode::handleForceDark(TreeInfo *info) {
+ if (CC_UNLIKELY(view_accessibility_flags::force_invert_color() && info &&
+ isForceInvertDark(*info))) {
+ mDisplayList.applyColorTransform(ColorTransform::Invert);
+ return;
+ }
if (!shouldEnableForceDark(info)) {
return;
}
@@ -427,13 +434,7 @@ void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
children.push_back(node);
});
if (mDisplayList.hasText()) {
- if (isForceInvertDark(*info) && mDisplayList.hasFill()) {
- // Handle a special case for custom views that draw both text and background in the
- // same RenderNode, which would otherwise be altered to white-on-white text.
- usage = UsageHint::Container;
- } else {
- usage = UsageHint::Foreground;
- }
+ usage = UsageHint::Foreground;
}
if (usage == UsageHint::Unknown) {
if (children.size() > 1) {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 63a024b8e780..3ef970830dc4 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -16,6 +16,8 @@
#include "Bitmap.h"
#include <android-base/file.h>
+
+#include "FeatureFlags.h"
#include "HardwareBitmapUploader.h"
#include "Properties.h"
#ifdef __ANDROID__ // Layoutlib does not support render thread
@@ -547,9 +549,16 @@ BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr,
}
ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = "
- "%f]",
+ "%f] %d x %d",
sampledCount, hue.min(), hue.max(), hue.average(), saturation.min(), saturation.max(),
- saturation.average());
+ saturation.average(), info.width(), info.height());
+
+ if (CC_UNLIKELY(view_accessibility_flags::force_invert_color())) {
+ if (saturation.delta() > 0.1f ||
+ (hue.delta() > 20 && saturation.average() > 0.2f && value.average() < 0.9f)) {
+ return BitmapPalette::Colorful;
+ }
+ }
if (hue.delta() <= 20 && saturation.delta() <= .1f) {
if (value.average() >= .5f) {
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 4e9bcf27c0ef..0fe5fe88f715 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -49,6 +49,7 @@ enum class BitmapPalette {
Unknown,
Light,
Dark,
+ Colorful,
};
namespace uirenderer {
diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp
index 6c82aa1ca27d..476b6fda5007 100644
--- a/libs/hwui/jni/GIFMovie.cpp
+++ b/libs/hwui/jni/GIFMovie.cpp
@@ -63,7 +63,7 @@ GIFMovie::GIFMovie(SkStream* stream)
}
fCurrIndex = -1;
fLastDrawIndex = -1;
- fPaintingColor = SkPackARGB32(0, 0, 0, 0);
+ fPaintingColor = SK_AlphaTRANSPARENT;
}
GIFMovie::~GIFMovie()
@@ -127,7 +127,7 @@ static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObje
for (; width > 0; width--, src++, dst++) {
if (*src != transparent && *src < cmap->ColorCount) {
const GifColorType& col = cmap->Colors[*src];
- *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
+ *dst = SkColorSetRGB(col.Red, col.Green, col.Blue);
}
}
}
@@ -395,10 +395,10 @@ bool GIFMovie::onGetBitmap(SkBitmap* bm)
lastIndex = fGIF->ImageCount - 1;
}
- SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
+ SkColor bgColor = SK_ColorTRANSPARENT;
if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) {
const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor];
- bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
+ bgColor = SkColorSetRGB(col.Red, col.Green, col.Blue);
}
// draw each frames - not intelligent way
@@ -411,7 +411,7 @@ bool GIFMovie::onGetBitmap(SkBitmap* bm)
if (!trans && gif->SColorMap != nullptr) {
fPaintingColor = bgColor;
} else {
- fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
+ fPaintingColor = SK_ColorTRANSPARENT;
}
bm->eraseColor(fPaintingColor);
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 80b55e2c1244..5a993bfcc9cf 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -33,6 +33,7 @@ import android.annotation.SystemApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.pm.PackageManager;
+import android.location.flags.Flags;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -181,7 +182,8 @@ public final class LocationRequest implements Parcelable {
public static final int POWER_HIGH = 203;
private static final long IMPLICIT_MIN_UPDATE_INTERVAL = -1;
- private static final double IMPLICIT_MIN_UPDATE_INTERVAL_FACTOR = 1D / 6D;
+ private static final double LEGACY_IMPLICIT_MIN_UPDATE_INTERVAL_FACTOR = 1D / 6D;
+ private static final double IMPLICIT_MIN_UPDATE_INTERVAL_FACTOR = 1D / 2D;
private @Nullable String mProvider;
private @Quality int mQuality;
@@ -553,7 +555,10 @@ public final class LocationRequest implements Parcelable {
*/
public @IntRange(from = 0) long getMinUpdateIntervalMillis() {
if (mMinUpdateIntervalMillis == IMPLICIT_MIN_UPDATE_INTERVAL) {
- return (long) (mIntervalMillis * IMPLICIT_MIN_UPDATE_INTERVAL_FACTOR);
+ if (Flags.updateMinLocationRequestInterval()) {
+ return (long) (mIntervalMillis * IMPLICIT_MIN_UPDATE_INTERVAL_FACTOR);
+ }
+ return (long) (mIntervalMillis * LEGACY_IMPLICIT_MIN_UPDATE_INTERVAL_FACTOR);
} else {
// the min is only necessary in case someone use a deprecated function to mess with the
// interval or min update interval
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 1b38982f48c1..83b1778fd611 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -168,3 +168,14 @@ flag {
description: "Flag for GNSS assistance interface"
bug: "209078566"
}
+
+flag {
+ name: "update_min_location_request_interval"
+ namespace: "location"
+ description: "Flag for updating the default logic for the minimal interval for location request"
+ bug: "397444378"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 2fa707e16fa2..897c1fe7639e 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -4,7 +4,6 @@ ronish@google.com
jsharkey@android.com
felipeal@google.com
nandana@google.com
-svetoslavganov@google.com
hackbod@google.com
yamasani@google.com
patb@google.com
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index b59b4ab34c80..06484128ed6c 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -103,6 +103,7 @@ open class ClockRegistry(
fun onAvailableClocksChanged() {}
}
+ private val replacementMap = ConcurrentHashMap<ClockId, ClockId>()
private val availableClocks = ConcurrentHashMap<ClockId, ClockInfo>()
private val clockChangeListeners = mutableListOf<ClockChangeListener>()
private val settingObserver =
@@ -209,6 +210,7 @@ open class ClockRegistry(
continue
}
+ clock.replacementTarget?.let { replacementMap[id] = it }
info.provider = plugin
onLoaded(info)
}
@@ -393,10 +395,11 @@ open class ClockRegistry(
// TODO: Merge w/ CurrentClockId when we convert to a flow. We shouldn't need both behaviors.
val activeClockId: String
get() {
- if (!availableClocks.containsKey(currentClockId)) {
+ var id = currentClockId
+ if (!availableClocks.containsKey(id)) {
return DEFAULT_CLOCK_ID
}
- return currentClockId
+ return replacementMap[id] ?: id
}
init {
@@ -404,6 +407,7 @@ open class ClockRegistry(
defaultClockProvider.initialize(clockBuffers)
for (clock in defaultClockProvider.getClocks()) {
availableClocks[clock.clockId] = ClockInfo(clock, defaultClockProvider, null)
+ clock.replacementTarget?.let { replacementMap[clock.clockId] = it }
}
// Something has gone terribly wrong if the default clock isn't present
@@ -562,9 +566,12 @@ open class ClockRegistry(
}
}
- fun getClocks(): List<ClockMetadata> {
- if (!isEnabled) return listOf(availableClocks[DEFAULT_CLOCK_ID]!!.metadata)
- return availableClocks.map { (_, clock) -> clock.metadata }
+ fun getClocks(includeDeprecated: Boolean = false): List<ClockMetadata> {
+ return when {
+ !isEnabled -> listOf(availableClocks[DEFAULT_CLOCK_ID]!!.metadata)
+ includeDeprecated -> availableClocks.map { (_, clock) -> clock.metadata }
+ else -> availableClocks.map { (_, clock) -> clock.metadata }.filter { !it.isDeprecated }
+ }
}
fun getClockPickerConfig(clockId: ClockId): ClockPickerConfig? {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 654478af3fb0..c3935e68ca04 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -61,7 +61,14 @@ class DefaultClockProvider(
override fun getClocks(): List<ClockMetadata> {
var clocks = listOf(ClockMetadata(DEFAULT_CLOCK_ID))
- if (isClockReactiveVariantsEnabled) clocks += ClockMetadata(FLEX_CLOCK_ID)
+ if (isClockReactiveVariantsEnabled) {
+ clocks +=
+ ClockMetadata(
+ FLEX_CLOCK_ID,
+ isDeprecated = true,
+ replacementTarget = DEFAULT_CLOCK_ID,
+ )
+ }
return clocks
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 189d554415d0..f4d4b1efa3e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -25,6 +25,8 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
+import android.view.ViewTreeObserver
+import android.view.ViewTreeObserver.OnPreDrawListener
import android.view.WindowInsetsController
import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -720,6 +722,37 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
}
@Test
+ fun startAppearAnimation_ifDelayed() {
+ val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java)
+ whenever(view.isAppearAnimationDelayed).thenReturn(true)
+ val viewTreeObserver: ViewTreeObserver = mock()
+ whenever(view.viewTreeObserver).thenReturn(viewTreeObserver)
+
+ underTest.startAppearAnimationIfDelayed()
+
+ verify(view).alpha = 1f
+ verify(viewTreeObserver).addOnPreDrawListener(argumentCaptor.capture())
+ argumentCaptor.value.onPreDraw()
+
+ verify(view).startAppearAnimation(any(SecurityMode::class.java))
+ verify(view).setIsAppearAnimationDelayed(false)
+ }
+
+ @Test
+ fun appearAnimation_willNotStart_ifNotDelayed() {
+ whenever(view.isAppearAnimationDelayed).thenReturn(false)
+ val viewTreeObserver: ViewTreeObserver = mock()
+ whenever(view.viewTreeObserver).thenReturn(viewTreeObserver)
+
+ underTest.startAppearAnimationIfDelayed()
+
+ verify(view, never()).alpha
+ verify(viewTreeObserver, never()).addOnPreDrawListener(any())
+
+ verify(view, never()).startAppearAnimation(any(SecurityMode::class.java))
+ }
+
+ @Test
fun gravityReappliedOnConfigurationChange() {
// Set initial gravity
testableResources.addOverride(R.integer.keyguard_host_view_gravity, Gravity.CENTER)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 176824fd4c5d..2845f6a2983a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -452,6 +452,14 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
verify(keyguardPasswordView).setDisappearAnimationListener(any());
}
+ @Test
+ public void setupForDelayedAppear() {
+ mKeyguardSecurityContainer.setupForDelayedAppear();
+ assertThat(mKeyguardSecurityContainer.getTranslationY()).isEqualTo(0f);
+ assertThat(mKeyguardSecurityContainer.getAlpha()).isEqualTo(0f);
+ assertThat(mKeyguardSecurityContainer.isAppearAnimationDelayed()).isTrue();
+ }
+
private BackEvent createBackEvent(float touchX, float progress) {
return new BackEvent(0, 0, progress, BackEvent.EDGE_LEFT);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
index f53f964cd3d9..191ecccd5f71 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
@@ -28,6 +28,8 @@ import static org.mockito.Mockito.when;
import android.animation.ValueAnimator;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
@@ -54,6 +56,7 @@ import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wm.shell.animation.FlingAnimationUtils;
import org.junit.Before;
@@ -127,10 +130,16 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
@Mock
WindowRootView mWindowRootView;
+ @Mock
+ Resources mResources;
+
private SceneInteractor mSceneInteractor;
+ private KeyguardStateController mKeyguardStateController;
+
private static final float TOUCH_REGION = .3f;
private static final float MIN_BOUNCER_HEIGHT = .05f;
+ private final Configuration mConfiguration = new Configuration();
private static final Rect SCREEN_BOUNDS = new Rect(0, 0, 1024, 100);
private static final UserInfo CURRENT_USER_INFO = new UserInfo(
@@ -153,6 +162,8 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
public void setup() {
mKosmos = new KosmosJavaAdapter(this);
mSceneInteractor = spy(mKosmos.getSceneInteractor());
+ mKeyguardStateController = mKosmos.getKeyguardStateController();
+ mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT;
MockitoAnnotations.initMocks(this);
mTouchHandler = new BouncerSwipeTouchHandler(
@@ -172,7 +183,9 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
mKeyguardInteractor,
mSceneInteractor,
mKosmos.getShadeRepository(),
- Optional.of(() -> mWindowRootView));
+ Optional.of(() -> mWindowRootView),
+ mKeyguardStateController,
+ mKosmos.getCommunalSettingsInteractor());
when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
@@ -180,6 +193,9 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE);
when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS);
when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(false));
+ when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true);
+ when(mWindowRootView.getResources()).thenReturn(mResources);
+ when(mResources.getConfiguration()).thenReturn(mConfiguration);
}
/**
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
index dd43d817cccc..e8dc6762cc92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.ambient.touch;
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2;
+
import static com.google.common.truth.Truth.assertThat;
import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
@@ -34,6 +38,8 @@ import static org.mockito.Mockito.when;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.Region;
import android.platform.test.annotations.DisableFlags;
@@ -63,6 +69,7 @@ import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wm.shell.animation.FlingAnimationUtils;
import org.junit.Before;
@@ -132,12 +139,16 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@Mock
WindowRootView mWindowRootView;
+ Resources mResources;
+
@Mock
CommunalViewModel mCommunalViewModel;
@Mock
KeyguardInteractor mKeyguardInteractor;
+ private KeyguardStateController mKeyguardStateController;
+
@Captor
ArgumentCaptor<Rect> mRectCaptor;
@@ -147,6 +158,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
private static final int SCREEN_WIDTH_PX = 1024;
private static final int SCREEN_HEIGHT_PX = 100;
private static final float MIN_BOUNCER_HEIGHT = .05f;
+ private final Configuration mConfiguration = new Configuration();
private static final Rect SCREEN_BOUNDS = new Rect(0, 0, 1024, 100);
private static final UserInfo CURRENT_USER_INFO = new UserInfo(
@@ -157,7 +169,8 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag();
+ return SceneContainerFlagParameterizationKt
+ .andSceneContainer(allCombinationsOf(Flags.FLAG_GLANCEABLE_HUB_V2));
}
public BouncerSwipeTouchHandlerTest(FlagsParameterization flags) {
@@ -168,7 +181,13 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@Before
public void setup() {
mKosmos = new KosmosJavaAdapter(this);
+ mContext.ensureTestableResources();
+ mResources = mContext.getResources();
+ overrideConfiguration(mConfiguration);
+ mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT;
+
mSceneInteractor = spy(mKosmos.getSceneInteractor());
+ mKeyguardStateController = mKosmos.getKeyguardStateController();
MockitoAnnotations.initMocks(this);
mTouchHandler = new BouncerSwipeTouchHandler(
@@ -188,7 +207,9 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
mKeyguardInteractor,
mSceneInteractor,
mKosmos.getShadeRepository(),
- Optional.of(() -> mWindowRootView)
+ Optional.of(() -> mWindowRootView),
+ mKeyguardStateController,
+ mKosmos.getCommunalSettingsInteractor()
);
when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
@@ -197,6 +218,9 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE);
when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS);
when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(false));
+ when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true);
+ when(mWindowRootView.getResources()).thenReturn(mResources);
+ setCommunalV2ConfigEnabled(true);
}
/**
@@ -586,6 +610,43 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_BOUNCER_FULLY_VISIBLE);
}
+ @Test
+ @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ public void swipeUpAboveThresholdInLandscape_keyguardRotationNotAllowed_showsBouncer() {
+ when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
+ mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+
+ final float swipeUpPercentage = .1f;
+ // The upward velocity is ignored.
+ final float velocityY = -1;
+ swipeToPosition(swipeUpPercentage, velocityY);
+
+ // Ensure show bouncer scrimmed
+ verify(mScrimController).show(true);
+ verify(mValueAnimatorCreator, never()).create(anyFloat(), anyFloat());
+ verify(mValueAnimator, never()).start();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ public void swipeUpBelowThreshold_inLandscapeKeyguardRotationNotAllowed_noBouncer() {
+ mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+
+ final float swipeUpPercentage = .02f;
+ // The upward velocity is ignored.
+ final float velocityY = -1;
+ swipeToPosition(swipeUpPercentage, velocityY);
+
+ // no bouncer shown scrimmed
+ verify(mScrimController, never()).show(true);
+ // on touch end, bouncer hidden
+ verify(mValueAnimatorCreator).create(eq(1 - swipeUpPercentage),
+ eq(KeyguardBouncerConstants.EXPANSION_HIDDEN));
+ verify(mValueAnimator, never()).addListener(any());
+ }
+
/**
* Tests that swiping up with a speed above the set threshold will continue the expansion.
*/
@@ -672,4 +733,15 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
inputEventListenerCaptor.getValue().onInputEvent(upEvent);
}
+
+ private void setCommunalV2ConfigEnabled(boolean enabled) {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ enabled);
+ }
+
+ private void overrideConfiguration(Configuration configuration) {
+ mContext.getOrCreateTestableResources().overrideConfiguration(
+ configuration);
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index 7051f81cfc88..f58391496e65 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -22,6 +22,7 @@ import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
@@ -38,8 +39,13 @@ import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.collectLastValue
@@ -56,11 +62,15 @@ import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.advanceTimeBy
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.kotlin.verify
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -93,10 +103,12 @@ class CommunalSceneStartableTest(flags: FlagsParameterization) : SysuiTestCase()
communalInteractor = communalInteractor,
communalSettingsInteractor = communalSettingsInteractor,
communalSceneInteractor = communalSceneInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
keyguardInteractor = keyguardInteractor,
systemSettings = fakeSettings,
notificationShadeWindowController = notificationShadeWindowController,
bgScope = applicationCoroutineScope,
+ applicationScope = applicationCoroutineScope,
mainDispatcher = testDispatcher,
uiEventLogger = uiEventLoggerFake,
)
@@ -111,13 +123,13 @@ class CommunalSceneStartableTest(flags: FlagsParameterization) : SysuiTestCase()
UserHandle.USER_CURRENT,
)
fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ setCommunalV2ConfigEnabled(true)
underTest.start()
// Make communal available so that communalInteractor.desiredScene accurately reflects
// scene changes instead of just returning Blank.
runBlocking { setCommunalAvailable(true) }
- setCommunalV2ConfigEnabled(true)
}
}
@@ -414,6 +426,107 @@ class CommunalSceneStartableTest(flags: FlagsParameterization) : SysuiTestCase()
assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
}
+ @Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun glanceableHubOrientationAware_idleOnCommunal() =
+ kosmos.runTest {
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
+
+ verify(notificationShadeWindowController).setGlanceableHubOrientationAware(true)
+ }
+
+ @Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun glanceableHubOrientationAware_transitioningToCommunal() =
+ kosmos.runTest {
+ val progress = MutableStateFlow(0f)
+ val targetScene = CommunalScenes.Communal
+ val currentScene = CommunalScenes.Blank
+ val transitionState =
+ MutableStateFlow(
+ ObservableTransitionState.Transition(
+ fromScene = currentScene,
+ toScene = targetScene,
+ currentScene = flowOf(targetScene),
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ communalSceneInteractor.setTransitionState(transitionState)
+
+ // Partially transition.
+ progress.value = .4f
+
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
+
+ verify(notificationShadeWindowController).setGlanceableHubOrientationAware(true)
+ }
+
+ @Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun glanceableHubOrientationAware_communalToDreaming() =
+ kosmos.runTest {
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+
+ verify(notificationShadeWindowController).setGlanceableHubOrientationAware(true)
+ Mockito.clearInvocations(notificationShadeWindowController)
+
+ val progress = MutableStateFlow(0f)
+ val currentScene = CommunalScenes.Communal
+ val targetScene = CommunalScenes.Blank
+ val transitionState =
+ MutableStateFlow(
+ ObservableTransitionState.Transition(
+ fromScene = currentScene,
+ toScene = targetScene,
+ currentScene = flowOf(targetScene),
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ communalSceneInteractor.setTransitionState(transitionState)
+
+ // Partially transitioned out of Communal scene
+ progress.value = .4f
+
+ // Started keyguard transitioning from hub -> dreaming.
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.DREAMING,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ verify(notificationShadeWindowController).setGlanceableHubOrientationAware(true)
+ Mockito.clearInvocations(notificationShadeWindowController)
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.DREAMING,
+ transitionState = TransitionState.RUNNING,
+ value = 0.5f,
+ )
+
+ // Transitioned to dreaming.
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.DREAMING,
+ transitionState = TransitionState.FINISHED,
+ value = 1f,
+ )
+ // Not on hub anymore, let other states take control
+ verify(notificationShadeWindowController).setGlanceableHubOrientationAware(false)
+ }
+
/**
* Advances time by duration + 1 millisecond, to ensure that tasks scheduled to run at
* currentTime + duration are scheduled.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
index 77d7091e463a..dc21f0692c9e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
@@ -134,7 +134,7 @@ class CommunalSceneInteractorTest(flags: FlagsParameterization) : SysuiTestCase(
underTest.snapToScene(
CommunalScenes.Communal,
"test",
- ActivityTransitionAnimator.TIMINGS.totalDuration
+ ActivityTransitionAnimator.TIMINGS.totalDuration,
)
assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
advanceTimeBy(ActivityTransitionAnimator.TIMINGS.totalDuration)
@@ -269,6 +269,48 @@ class CommunalSceneInteractorTest(flags: FlagsParameterization) : SysuiTestCase(
@DisableFlags(FLAG_SCENE_CONTAINER)
@Test
+ fun isTransitioningToOrIdleOnCommunal() =
+ testScope.runTest {
+ // isIdleOnCommunal is false when not on communal.
+ val isTransitioningToOrIdleOnCommunal by
+ collectLastValue(underTest.isTransitioningToOrIdleOnCommunal)
+ assertThat(isTransitioningToOrIdleOnCommunal).isEqualTo(false)
+
+ val transitionState: MutableStateFlow<ObservableTransitionState> =
+ MutableStateFlow(
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Blank,
+ toScene = CommunalScenes.Communal,
+ currentScene = flowOf(CommunalScenes.Communal),
+ progress = flowOf(0f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+
+ // Start transition to communal.
+ repository.setTransitionState(transitionState)
+ assertThat(isTransitioningToOrIdleOnCommunal).isEqualTo(true)
+
+ // Finish transition to communal
+ transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal)
+ assertThat(isTransitioningToOrIdleOnCommunal).isEqualTo(true)
+
+ // Start transition away from communal.
+ transitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Communal,
+ toScene = CommunalScenes.Blank,
+ currentScene = flowOf(CommunalScenes.Blank),
+ progress = flowOf(.1f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ assertThat(isTransitioningToOrIdleOnCommunal).isEqualTo(false)
+ }
+
+ @DisableFlags(FLAG_SCENE_CONTAINER)
+ @Test
fun isCommunalVisible() =
testScope.runTest {
// isCommunalVisible is false when not on communal.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelTest.kt
index 6b9e23abd9a4..135e9a55e8b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelTest.kt
@@ -16,28 +16,46 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.content.res.mainResources
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.transitions.blurConfig
+import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
+import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class GlanceableHubToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos =
+ testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources }
private val underTest by lazy { kosmos.glanceableHubToPrimaryBouncerTransitionViewModel }
+ @Before
+ fun setUp() {
+ with(kosmos) { setCommunalV2ConfigEnabled(true) }
+ }
+
@Test
@DisableSceneContainer
@DisableFlags(FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
@@ -84,4 +102,81 @@ class GlanceableHubToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
},
)
}
+
+ @Test
+ @DisableSceneContainer
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun willDelayBouncerAppearAnimation_flagDisabled_isFalse() =
+ kosmos.runTest {
+ // keyguard rotation is not allowed on device.
+ whenever(keyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false)
+
+ val isIdleOnCommunal by collectLastValue(communalInteractor.isIdleOnCommunal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ runCurrent()
+ // Device is idle on communal.
+ assertThat(isIdleOnCommunal).isTrue()
+
+ // in landscape
+ assertThat(underTest.willDelayAppearAnimation(isLandscape = true)).isFalse()
+ // in portrait
+ assertThat(underTest.willDelayAppearAnimation(isLandscape = false)).isFalse()
+ }
+
+ @Test
+ @DisableSceneContainer
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun willDelayBouncerAppearAnimation_keyguardRotationAllowed_isFalse() =
+ kosmos.runTest {
+ // Keyguard rotation is allowed on device.
+ whenever(keyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true)
+
+ val isIdleOnCommunal by collectLastValue(communalInteractor.isIdleOnCommunal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ runCurrent()
+ // Device is idle on communal.
+ assertThat(isIdleOnCommunal).isTrue()
+
+ // in landscape
+ assertThat(underTest.willDelayAppearAnimation(isLandscape = true)).isFalse()
+ // in portrait
+ assertThat(underTest.willDelayAppearAnimation(isLandscape = false)).isFalse()
+ }
+
+ @Test
+ @DisableSceneContainer
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun willDelayBouncerAppearAnimation_isNotIdleOnCommunal_isFalse() =
+ kosmos.runTest {
+ whenever(keyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false)
+
+ val isIdleOnCommunal by collectLastValue(communalInteractor.isIdleOnCommunal)
+ communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
+ runCurrent()
+ // Device is not on communal.
+ assertThat(isIdleOnCommunal).isFalse()
+
+ // in landscape
+ assertThat(underTest.willDelayAppearAnimation(isLandscape = true)).isFalse()
+ // in portrait
+ assertThat(underTest.willDelayAppearAnimation(isLandscape = false)).isFalse()
+ }
+
+ @Test
+ @DisableSceneContainer
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun willDelayBouncerAppearAnimation_isIdleOnCommunalAndKeyguardRotationIsNotAllowed() =
+ kosmos.runTest {
+ whenever(keyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false)
+ val isIdleOnCommunal by collectLastValue(communalInteractor.isIdleOnCommunal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ runCurrent()
+ // Device is idle on communal.
+ assertThat(isIdleOnCommunal).isTrue()
+
+ // Will delay in landscape
+ assertThat(underTest.willDelayAppearAnimation(isLandscape = true)).isTrue()
+ // Won't delay in portrait
+ assertThat(underTest.willDelayAppearAnimation(isLandscape = false)).isFalse()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 764068ec1bf5..3407cd50e76f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -84,12 +84,12 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
-import java.util.List;
-import java.util.concurrent.Executor;
-
import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
import platform.test.runner.parameterized.Parameters;
+import java.util.List;
+import java.util.concurrent.Executor;
+
@RunWith(ParameterizedAndroidJunit4.class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
@@ -410,6 +410,19 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
}
@Test
+ public void hubOrientationAware_layoutParamsUpdated() {
+ mNotificationShadeWindowController.setKeyguardShowing(false);
+ mNotificationShadeWindowController.setBouncerShowing(false);
+ mNotificationShadeWindowController.setGlanceableHubOrientationAware(true);
+ when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
+ mNotificationShadeWindowController.onConfigChanged(new Configuration());
+
+ verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture());
+ assertThat(mLayoutParameters.getValue().screenOrientation)
+ .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_USER);
+ }
+
+ @Test
public void batchApplyWindowLayoutParams_doesNotDispatchEvents() {
mNotificationShadeWindowController.setForceDozeBrightness(true);
verify(mWindowManager).updateViewLayout(any(), any());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index f4a43a454a6f..ddad230f04e9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -151,9 +151,17 @@ class ClockRegistryTest : SysuiTestCase() {
create: (ClockId) -> ClockController = ::failFactory,
getPickerConfig: (ClockSettings) -> ClockPickerConfig = ::failPickerConfig,
): FakeClockPlugin {
- metadata.add(ClockMetadata(id))
- createCallbacks[id] = create
- pickerConfigs[id] = getPickerConfig
+ return addClock(ClockMetadata(id), create, getPickerConfig)
+ }
+
+ fun addClock(
+ metadata: ClockMetadata,
+ create: (ClockId) -> ClockController = ::failFactory,
+ getPickerConfig: (ClockSettings) -> ClockPickerConfig = ::failPickerConfig,
+ ): FakeClockPlugin {
+ this.metadata.add(metadata)
+ createCallbacks[metadata.clockId] = create
+ pickerConfigs[metadata.clockId] = getPickerConfig
return this
}
}
@@ -203,28 +211,40 @@ class ClockRegistryTest : SysuiTestCase() {
val plugin1 = FakeClockPlugin().addClock("clock_1").addClock("clock_2")
val lifecycle1 = FakeLifecycle("1", plugin1)
- val plugin2 = FakeClockPlugin().addClock("clock_3").addClock("clock_4")
+ val plugin2 =
+ FakeClockPlugin()
+ .addClock(ClockMetadata("clock_3", isDeprecated = false))
+ .addClock(ClockMetadata("clock_4", isDeprecated = true))
val lifecycle2 = FakeLifecycle("2", plugin2)
pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
- val list = registry.getClocks()
assertEquals(
- list.toSet(),
setOf(
ClockMetadata(DEFAULT_CLOCK_ID),
ClockMetadata("clock_1"),
ClockMetadata("clock_2"),
ClockMetadata("clock_3"),
- ClockMetadata("clock_4"),
),
+ registry.getClocks().toSet(),
+ )
+
+ assertEquals(
+ setOf(
+ ClockMetadata(DEFAULT_CLOCK_ID),
+ ClockMetadata("clock_1"),
+ ClockMetadata("clock_2"),
+ ClockMetadata("clock_3"),
+ ClockMetadata("clock_4", isDeprecated = true),
+ ),
+ registry.getClocks(includeDeprecated = true).toSet(),
)
}
@Test
fun noPlugins_createDefaultClock() {
val clock = registry.createCurrentClock()
- assertEquals(clock, mockDefaultClock)
+ assertEquals(mockDefaultClock, clock)
}
@Test
@@ -242,18 +262,18 @@ class ClockRegistryTest : SysuiTestCase() {
pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
val list = registry.getClocks()
assertEquals(
- list.toSet(),
setOf(
ClockMetadata(DEFAULT_CLOCK_ID),
ClockMetadata("clock_1"),
ClockMetadata("clock_2"),
),
+ list.toSet(),
)
- assertEquals(registry.createExampleClock("clock_1"), mockClock)
- assertEquals(registry.createExampleClock("clock_2"), mockClock)
- assertEquals(registry.getClockPickerConfig("clock_1"), pickerConfig)
- assertEquals(registry.getClockPickerConfig("clock_2"), pickerConfig)
+ assertEquals(mockClock, registry.createExampleClock("clock_1"))
+ assertEquals(mockClock, registry.createExampleClock("clock_2"))
+ assertEquals(pickerConfig, registry.getClockPickerConfig("clock_1"))
+ assertEquals(pickerConfig, registry.getClockPickerConfig("clock_2"))
verify(lifecycle1, never()).unloadPlugin()
verify(lifecycle2, times(2)).unloadPlugin()
}
@@ -305,7 +325,7 @@ class ClockRegistryTest : SysuiTestCase() {
pluginListener.onPluginUnloaded(plugin2, lifecycle2)
val clock = registry.createCurrentClock()
- assertEquals(clock, mockDefaultClock)
+ assertEquals(mockDefaultClock, clock)
}
@Test
@@ -482,13 +502,13 @@ class ClockRegistryTest : SysuiTestCase() {
// Verify all plugins were correctly loaded into the registry
assertEquals(
- registry.getClocks().toSet(),
setOf(
ClockMetadata("DEFAULT"),
ClockMetadata("clock_2"),
ClockMetadata("clock_3"),
ClockMetadata("clock_4"),
),
+ registry.getClocks().toSet(),
)
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 7426f061b84c..0ef62a32a9c4 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -53,5 +53,21 @@ interface ClockProvider {
/** Identifies a clock design */
typealias ClockId = String
-/** Some data about a clock design */
-data class ClockMetadata(val clockId: ClockId)
+/** Some metadata about a clock design */
+data class ClockMetadata(
+ /** Id for the clock design. */
+ val clockId: ClockId,
+
+ /**
+ * true if this clock is deprecated and should not be used. The ID may still show up in certain
+ * locations to help migrations, but it will not be selectable by new users.
+ */
+ val isDeprecated: Boolean = false,
+
+ /**
+ * Optional mapping of a legacy clock to a new id. This will map users that already are using
+ * `clockId` to the `replacementTarget` instead. The provider should still support the old id
+ * w/o crashing, but can consider it deprecated and the id reserved.
+ */
+ val replacementTarget: ClockId? = null,
+)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 73dc28230e65..e2f3955263a1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -172,6 +172,7 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
private boolean mIsDragging;
private float mStartTouchY = -1;
private boolean mDisappearAnimRunning;
+ private boolean mIsAppearAnimationDelayed;
private SwipeListener mSwipeListener;
private ViewMode mViewMode = new DefaultViewMode();
private boolean mIsInteractable;
@@ -583,6 +584,10 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
return false;
}
+ boolean isAppearAnimationDelayed() {
+ return mIsAppearAnimationDelayed;
+ }
+
void addMotionEventListener(Gefingerpoken listener) {
mMotionEventListeners.add(listener);
}
@@ -624,6 +629,19 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
mViewMode.startAppearAnimation(securityMode);
}
+ /**
+ * Set view translationY and alpha as we delay bouncer animation.
+ */
+ public void setupForDelayedAppear() {
+ setTranslationY(0f);
+ setAlpha(0f);
+ setIsAppearAnimationDelayed(true);
+ }
+
+ public void setIsAppearAnimationDelayed(boolean isDelayed) {
+ mIsAppearAnimationDelayed = isDelayed;
+ }
+
private void beginJankInstrument(int cuj) {
KeyguardInputView securityView = mSecurityViewFlipper.getSecurityView();
if (securityView == null) return;
@@ -812,6 +830,7 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
public void reset() {
mViewMode.reset();
mDisappearAnimRunning = false;
+ mIsAppearAnimationDelayed = false;
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index d10fce416150..198c1cb08647 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -18,6 +18,7 @@ package com.android.keyguard;
import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISSIBLE_KEYGUARD;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
@@ -385,6 +386,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
boolean useSplitBouncer = orientation == ORIENTATION_LANDSCAPE;
mSecurityViewFlipperController.updateConstraints(useSplitBouncer);
}
+ if (orientation == ORIENTATION_PORTRAIT) {
+ // If there is any delayed bouncer appear animation it can start now
+ startAppearAnimationIfDelayed();
+ }
}
@Override
@@ -845,6 +850,16 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
}
+ /** Start appear animation which was previously delayed from opening bouncer in landscape. */
+ public void startAppearAnimationIfDelayed() {
+ if (!mView.isAppearAnimationDelayed()) {
+ return;
+ }
+ setAlpha(1f);
+ appear();
+ mView.setIsAppearAnimationDelayed(false);
+ }
+
/** Called when the bouncer changes visibility. */
public void onBouncerVisibilityChanged(boolean isVisible) {
if (!isVisible) {
@@ -1301,4 +1316,13 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
setAlpha(MathUtils.constrain(1 - scaledFraction, 0f, 1f));
mView.setTranslationY(scaledFraction * mTranslationY);
}
+
+ /** Set up view for delayed appear animation. */
+ public void setupForDelayedAppear() {
+ mView.setupForDelayedAppear();
+ }
+
+ public boolean isLandscapeOrientation() {
+ return mLastOrientation == Configuration.ORIENTATION_LANDSCAPE;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
index d8e7a168ef3c..97de78c41af7 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
@@ -18,6 +18,7 @@ package com.android.systemui.ambient.touch
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
+import android.content.res.Configuration
import android.graphics.Rect
import android.graphics.Region
import android.util.Log
@@ -36,6 +37,7 @@ import com.android.systemui.ambient.touch.dagger.BouncerSwipeModule
import com.android.systemui.ambient.touch.scrim.ScrimController
import com.android.systemui.ambient.touch.scrim.ScrimManager
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
@@ -46,6 +48,7 @@ import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.wm.shell.animation.FlingAnimationUtils
import java.util.Optional
import javax.inject.Inject
@@ -82,6 +85,8 @@ constructor(
private val sceneInteractor: SceneInteractor,
private val shadeRepository: ShadeRepository,
private val windowRootViewProvider: Optional<Provider<WindowRootView>>,
+ private val keyguardStateController: KeyguardStateController,
+ communalSettingsInteractor: CommunalSettingsInteractor,
) : TouchHandler {
/** An interface for creating ValueAnimators. */
interface ValueAnimatorCreator {
@@ -101,6 +106,8 @@ constructor(
private var capture: Boolean? = null
private var expanded: Boolean = false
private var touchSession: TouchSession? = null
+ private var isUserTrackingExpansionDisabled: Boolean = false
+ private var isKeyguardScreenRotationAllowed: Boolean = false
private val scrimManagerCallback =
ScrimManager.Callback { controller ->
currentScrimController?.reset()
@@ -121,6 +128,9 @@ constructor(
distanceX: Float,
distanceY: Float,
): Boolean {
+ val isLandscape =
+ windowRootView.resources.configuration.orientation ==
+ Configuration.ORIENTATION_LANDSCAPE
if (capture == null) {
capture =
if (Flags.dreamOverlayBouncerSwipeDirectionFiltering()) {
@@ -137,7 +147,9 @@ constructor(
// reset expanding
expanded = false
// Since the user is dragging the bouncer up, set scrimmed to false.
- currentScrimController?.show()
+ if (isKeyguardScreenRotationAllowed || !isLandscape) {
+ currentScrimController?.show(false)
+ }
if (SceneContainerFlag.isEnabled) {
sceneInteractor.onRemoteUserInputStarted("bouncer touch handler")
@@ -172,6 +184,37 @@ constructor(
return true
}
+ if (touchSession == null) {
+ return true
+ }
+ val screenTravelPercentage =
+ (abs((y - e2.y).toDouble()) / touchSession!!.bounds.height()).toFloat()
+
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ if (isUserTrackingExpansionDisabled) return true
+ // scrolling up in landscape orientation but device doesn't allow keyguard
+ // screen rotation
+ if (y > e2.y && !isKeyguardScreenRotationAllowed && isLandscape) {
+ velocityTracker!!.computeCurrentVelocity(1000)
+ currentExpansion = 1 - screenTravelPercentage
+ expanded =
+ shouldExpandBouncer(
+ velocityTracker!!.yVelocity,
+ velocityTracker!!.xVelocity,
+ EXPANSION_FROM_LANDSCAPE_THRESHOLD,
+ currentExpansion,
+ )
+ if (expanded) {
+ // Once scroll past the percentage threshold, show bouncer scrimmed,
+ // so that user won't be required to drag up and then right to keep
+ // bouncer open after screen rotates to portrait.
+ currentScrimController?.show(true)
+ isUserTrackingExpansionDisabled = true
+ }
+ return true
+ }
+ }
+
if (SceneContainerFlag.isEnabled) {
windowRootView.dispatchTouchEvent(e2)
} else {
@@ -182,12 +225,7 @@ constructor(
// is fully hidden at full expansion (1) and fully visible when fully
// collapsed
// (0).
- touchSession?.apply {
- val screenTravelPercentage =
- (abs((this@outer.y - e2.y).toDouble()) / getBounds().height())
- .toFloat()
- setPanelExpansion(1 - screenTravelPercentage)
- }
+ touchSession?.apply { setPanelExpansion(1 - screenTravelPercentage) }
}
}
@@ -262,6 +300,7 @@ constructor(
}
scrimManager.addCallback(scrimManagerCallback)
currentScrimController = scrimManager.currentController
+ isKeyguardScreenRotationAllowed = keyguardStateController.isKeyguardScreenRotationAllowed()
shadeRepository.setLegacyShadeTracking(true)
session.registerCallback {
@@ -271,6 +310,7 @@ constructor(
scrimManager.removeCallback(scrimManagerCallback)
capture = null
touchSession = null
+ isUserTrackingExpansionDisabled = false
if (!Flags.communalBouncerDoNotModifyPluginOpen()) {
notificationShadeWindowController.setForcePluginOpen(false, this)
}
@@ -299,14 +339,25 @@ constructor(
return
}
+ // We are already in progress of opening bouncer scrimmed
+ if (isUserTrackingExpansionDisabled) {
+ // User is done scrolling, reset
+ isUserTrackingExpansionDisabled = false
+ return
+ }
+
// We must capture the resulting velocities as resetMonitor() will clear these
// values.
velocityTracker!!.computeCurrentVelocity(1000)
val verticalVelocity = velocityTracker!!.yVelocity
- val horizontalVelocity = velocityTracker!!.xVelocity
- val velocityVector =
- hypot(horizontalVelocity.toDouble(), verticalVelocity.toDouble()).toFloat()
- expanded = !flingRevealsOverlay(verticalVelocity, velocityVector)
+ expanded =
+ shouldExpandBouncer(
+ verticalVelocity,
+ velocityTracker!!.xVelocity,
+ FLING_PERCENTAGE_THRESHOLD,
+ currentExpansion,
+ )
+
val expansion =
if (expanded!!) KeyguardBouncerConstants.EXPANSION_VISIBLE
else KeyguardBouncerConstants.EXPANSION_HIDDEN
@@ -339,11 +390,27 @@ constructor(
return animator
}
- protected fun flingRevealsOverlay(velocity: Float, velocityVector: Float): Boolean {
+ private fun shouldExpandBouncer(
+ verticalVelocity: Float,
+ horizontalVelocity: Float,
+ threshold: Float,
+ expansion: Float,
+ ): Boolean {
+ val velocityVector =
+ hypot(horizontalVelocity.toDouble(), verticalVelocity.toDouble()).toFloat()
+ return !flingRevealsOverlay(verticalVelocity, velocityVector, threshold, expansion)
+ }
+
+ protected fun flingRevealsOverlay(
+ velocity: Float,
+ velocityVector: Float,
+ threshold: Float,
+ expansion: Float,
+ ): Boolean {
// Fully expand the space above the bouncer, if the user has expanded the bouncer less
// than halfway or final velocity was positive, indicating a downward direction.
return if (abs(velocityVector.toDouble()) < flingAnimationUtils.minVelocityPxPerSecond) {
- currentExpansion > FLING_PERCENTAGE_THRESHOLD
+ expansion > threshold
} else {
velocity > 0
}
@@ -390,6 +457,7 @@ constructor(
companion object {
const val FLING_PERCENTAGE_THRESHOLD = 0.5f
+ const val EXPANSION_FROM_LANDSCAPE_THRESHOLD = 0.95f
private const val TAG = "BouncerSwipeTouchHandler"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java
index 94c998267598..6f2dd799c409 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java
@@ -33,8 +33,8 @@ public class BouncerScrimController implements ScrimController {
}
@Override
- public void show() {
- mStatusBarKeyguardViewManager.showPrimaryBouncer(false);
+ public void show(boolean scrimmed) {
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(scrimmed);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/ScrimController.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/ScrimController.java
index 00543523ec2e..90cbd258f03e 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/ScrimController.java
@@ -25,8 +25,9 @@ import com.android.systemui.shade.ShadeExpansionChangeEvent;
public interface ScrimController {
/**
* Called at the start of expansion before any expansion amount updates.
+ * @param scrimmed true when the bouncer should show scrimmed, false when user will be dragging.
*/
- default void show() {
+ default void show(boolean scrimmed) {
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
index 7f268315e566..5d64219c7f90 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
@@ -15,6 +15,7 @@ import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -30,6 +31,8 @@ data class LegacyBouncerDependencies
constructor(
val viewModel: KeyguardBouncerViewModel,
val primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
+ val glanceableHubToPrimaryBouncerTransitionViewModel:
+ GlanceableHubToPrimaryBouncerTransitionViewModel,
val componentFactory: KeyguardBouncerComponent.Factory,
val messageAreaControllerFactory: KeyguardMessageAreaController.Factory,
val bouncerMessageInteractor: BouncerMessageInteractor,
@@ -82,6 +85,7 @@ constructor(
view,
deps.viewModel,
deps.primaryBouncerToGoneTransitionViewModel,
+ deps.glanceableHubToPrimaryBouncerTransitionViewModel,
deps.componentFactory,
deps.messageAreaControllerFactory,
deps.bouncerMessageInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index 7d8945a5b4a7..45f0e13c185e 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -33,6 +33,7 @@ import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
import com.android.systemui.bouncer.ui.BouncerViewDelegate
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.BouncerLogger
@@ -49,6 +50,8 @@ object KeyguardBouncerViewBinder {
view: ViewGroup,
viewModel: KeyguardBouncerViewModel,
primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
+ glanceableHubToPrimaryBouncerTransitionViewModel:
+ GlanceableHubToPrimaryBouncerTransitionViewModel,
componentFactory: KeyguardBouncerComponent.Factory,
messageAreaControllerFactory: KeyguardMessageAreaController.Factory,
bouncerMessageInteractor: BouncerMessageInteractor,
@@ -133,7 +136,20 @@ object KeyguardBouncerViewBinder {
/* turningOff= */ false
)
securityContainerController.setInitialMessage()
- securityContainerController.appear()
+ // Delay bouncer appearing animation when opening it from the
+ // glanceable hub in landscape, until after orientation changes
+ // to portrait. This prevents bouncer from showing in landscape
+ // layout, if bouncer rotation is not allowed.
+ if (
+ glanceableHubToPrimaryBouncerTransitionViewModel
+ .willDelayAppearAnimation(
+ securityContainerController.isLandscapeOrientation
+ )
+ ) {
+ securityContainerController.setupForDelayedAppear()
+ } else {
+ securityContainerController.appear()
+ }
securityContainerController.onResume(
KeyguardSecurityView.SCREEN_ON
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index e36e85565293..49b0bb63545f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -29,10 +29,17 @@ import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalScenes.isCommunal
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.kotlin.emitOnStart
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
@@ -45,6 +52,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
@@ -60,10 +68,12 @@ constructor(
private val communalInteractor: CommunalInteractor,
private val communalSettingsInteractor: CommunalSettingsInteractor,
private val communalSceneInteractor: CommunalSceneInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val systemSettings: SystemSettings,
private val notificationShadeWindowController: NotificationShadeWindowController,
@Background private val bgScope: CoroutineScope,
+ @Application private val applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
private val uiEventLogger: UiEventLogger,
) : CoreStartable {
@@ -154,6 +164,25 @@ constructor(
}
}
}
+
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ applicationScope.launch(context = mainDispatcher) {
+ anyOf(
+ communalSceneInteractor.isTransitioningToOrIdleOnCommunal,
+ // when transitioning from hub to dream, allow hub to stay at the current
+ // orientation, as keyguard doesn't allow rotation by default.
+ keyguardTransitionInteractor.isInTransition(
+ edge = Edge.create(from = Scenes.Communal, to = DREAMING),
+ edgeWithoutSceneContainer =
+ Edge.create(from = GLANCEABLE_HUB, to = DREAMING),
+ ),
+ )
+ .distinctUntilChanged()
+ .collectLatest {
+ notificationShadeWindowController.setGlanceableHubOrientationAware(it)
+ }
+ }
+ }
}
private fun cancelHubTimeout() {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index 3d9e93036dbc..fed99d71fa3b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -307,6 +307,21 @@ constructor(
initialValue = false,
)
+ /** Flow that emits a boolean if transitioning to or idle on communal scene. */
+ val isTransitioningToOrIdleOnCommunal: Flow<Boolean> =
+ transitionState
+ .map {
+ (it is ObservableTransitionState.Idle &&
+ it.currentScene == CommunalScenes.Communal) ||
+ (it is ObservableTransitionState.Transition &&
+ it.toContent == CommunalScenes.Communal)
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
private companion object {
const val TAG = "CommunalSceneInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt
index 40010548a268..c088900f9304 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt
@@ -17,33 +17,39 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.Flags
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.BlurConfig
import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class GlanceableHubToPrimaryBouncerTransitionViewModel
@Inject
constructor(
private val blurConfig: BlurConfig,
animationFlow: KeyguardTransitionAnimationFlow,
- communalSettingsInteractor: CommunalSettingsInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
+ private val communalSceneInteractor: CommunalSceneInteractor,
+ private val keyguardStateController: KeyguardStateController,
) : PrimaryBouncerTransition {
private val transitionAnimation =
animationFlow
.setup(
- duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ duration = FromGlanceableHubTransitionInteractor.TO_BOUNCER_DURATION,
edge = Edge.INVALID,
)
.setupWithoutSceneContainer(edge = Edge.create(GLANCEABLE_HUB, PRIMARY_BOUNCER))
@@ -59,6 +65,13 @@ constructor(
transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
}
+ /** Whether to delay the animation to fade in bouncer elements. */
+ fun willDelayAppearAnimation(isLandscape: Boolean): Boolean =
+ communalSettingsInteractor.isV2FlagEnabled() &&
+ communalSceneInteractor.isIdleOnCommunal.value &&
+ !keyguardStateController.isKeyguardScreenRotationAllowed() &&
+ isLandscape
+
override val notificationBlurRadius: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0.0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index e4cd7ea098af..305444f7ab5e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -451,6 +451,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
} else {
mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
}
+ } else if (state.glanceableHubOrientationAware) {
+ mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
} else {
mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
@@ -627,6 +629,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
state.shadeOrQsExpanded,
state.notificationShadeFocusable,
state.glanceableHubShowing,
+ state.glanceableHubOrientationAware,
state.bouncerShowing,
state.keyguardFadingAway,
state.keyguardGoingAway,
@@ -763,6 +766,12 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
@Override
+ public void setGlanceableHubOrientationAware(boolean isOrientationAware) {
+ mCurrentState.glanceableHubOrientationAware = isOrientationAware;
+ apply(mCurrentState);
+ }
+
+ @Override
public void setBackdropShowing(boolean showing) {
mCurrentState.mediaBackdropShowing = showing;
apply(mCurrentState);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
index 6a4b52af498c..a1eac745b3a1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -36,6 +36,7 @@ class NotificationShadeWindowState(
@JvmField var notificationShadeFocusable: Boolean = false,
@JvmField var bouncerShowing: Boolean = false,
@JvmField var glanceableHubShowing: Boolean = false,
+ @JvmField var glanceableHubOrientationAware: Boolean = false,
@JvmField var keyguardFadingAway: Boolean = false,
@JvmField var keyguardGoingAway: Boolean = false,
@JvmField var qsExpanded: Boolean = false,
@@ -81,6 +82,7 @@ class NotificationShadeWindowState(
notificationShadeFocusable.toString(),
bouncerShowing.toString(),
glanceableHubShowing.toString(),
+ glanceableHubOrientationAware.toString(),
keyguardFadingAway.toString(),
keyguardGoingAway.toString(),
qsExpanded.toString(),
@@ -122,6 +124,7 @@ class NotificationShadeWindowState(
panelExpanded: Boolean,
notificationShadeFocusable: Boolean,
glanceableHubShowing: Boolean,
+ glanceableHubOrientationAware: Boolean,
bouncerShowing: Boolean,
keyguardFadingAway: Boolean,
keyguardGoingAway: Boolean,
@@ -153,6 +156,7 @@ class NotificationShadeWindowState(
this.shadeOrQsExpanded = panelExpanded
this.notificationShadeFocusable = notificationShadeFocusable
this.glanceableHubShowing = glanceableHubShowing
+ this.glanceableHubOrientationAware = glanceableHubOrientationAware
this.bouncerShowing = bouncerShowing
this.keyguardFadingAway = keyguardFadingAway
this.keyguardGoingAway = keyguardGoingAway
@@ -202,6 +206,7 @@ class NotificationShadeWindowState(
"panelExpanded",
"notificationShadeFocusable",
"glanceableHubShowing",
+ "glanceableHubOrientationAware",
"bouncerShowing",
"keyguardFadingAway",
"keyguardGoingAway",
@@ -223,7 +228,7 @@ class NotificationShadeWindowState(
"dozing",
"scrimsVisibility",
"backgroundBlurRadius",
- "communalVisible"
+ "communalVisible",
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 85fad420daf1..50cf015af5e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -89,6 +89,9 @@ public interface NotificationShadeWindowController extends RemoteInputController
/** Sets the state of whether the glanceable hub is showing or not. */
default void setGlanceableHubShowing(boolean showing) {}
+ /** Sets the state of whether the glanceable hub can change with user's orientation or not. */
+ default void setGlanceableHubOrientationAware(boolean isOrientationAware) {}
+
/** Sets the state of whether the backdrop is showing or not. */
default void setBackdropShowing(boolean showing) {}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelKosmos.kt
index b233d3ff9e0f..c6f55f053d35 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModelKosmos.kt
@@ -16,16 +16,20 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.blurConfig
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.policy.keyguardStateController
val Kosmos.glanceableHubToPrimaryBouncerTransitionViewModel by Fixture {
GlanceableHubToPrimaryBouncerTransitionViewModel(
animationFlow = keyguardTransitionAnimationFlow,
blurConfig = blurConfig,
communalSettingsInteractor = communalSettingsInteractor,
+ communalSceneInteractor = communalSceneInteractor,
+ keyguardStateController = keyguardStateController,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 02cf1f5a7214..dff9f3abfc05 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -29,6 +29,7 @@ import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -87,6 +88,7 @@ import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.wifiIntera
import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
+import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.statusbar.ui.viewmodel.keyguardStatusBarViewModel
import com.android.systemui.util.time.systemClock
import com.android.systemui.volume.domain.interactor.volumeDialogInteractor
@@ -126,6 +128,7 @@ class KosmosJavaAdapter() {
val keyguardInteractor by lazy { kosmos.keyguardInteractor }
val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
val keyguardTransitionInteractor by lazy { kosmos.keyguardTransitionInteractor }
+ val keyguardStateController by lazy { kosmos.keyguardStateController }
val keyguardStatusBarViewModel by lazy { kosmos.keyguardStatusBarViewModel }
val powerRepository by lazy { kosmos.fakePowerRepository }
val clock by lazy { kosmos.systemClock }
@@ -147,6 +150,7 @@ class KosmosJavaAdapter() {
val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
val communalInteractor by lazy { kosmos.communalInteractor }
val communalSceneInteractor by lazy { kosmos.communalSceneInteractor }
+ val communalSettingsInteractor by lazy { kosmos.communalSettingsInteractor }
val sceneContainerPlugin by lazy { kosmos.sceneContainerPlugin }
val deviceProvisioningInteractor by lazy { kosmos.deviceProvisioningInteractor }
val fakeDeviceProvisioningRepository by lazy { kosmos.fakeDeviceProvisioningRepository }
diff --git a/proto/src/metrics_constants/OWNERS b/proto/src/metrics_constants/OWNERS
index b032841228d1..169f887ede56 100644
--- a/proto/src/metrics_constants/OWNERS
+++ b/proto/src/metrics_constants/OWNERS
@@ -1,3 +1,2 @@
cwren@android.com
yaochen@google.com
-yro@google.com
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index bb3c710b0c23..0f6f86b39458 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -103,12 +103,16 @@ public class AutoclickController extends BaseEventStreamTransformation {
@Override
public void toggleAutoclickPause(boolean paused) {
if (paused) {
- if (mClickScheduler != null) {
- mClickScheduler.cancel();
- }
- if (mAutoclickIndicatorScheduler != null) {
- mAutoclickIndicatorScheduler.cancel();
- }
+ cancelPendingClick();
+ }
+ }
+
+ @Override
+ public void onHoverChange(boolean hovered) {
+ // Cancel all pending clicks when the mouse moves outside the panel while
+ // autoclick is still paused.
+ if (!hovered && isPaused()) {
+ cancelPendingClick();
}
}
};
@@ -226,8 +230,17 @@ public class AutoclickController extends BaseEventStreamTransformation {
}
private boolean isPaused() {
- // TODO (b/397460424): Unpause when hovering over panel.
- return Flags.enableAutoclickIndicator() && mAutoclickTypePanel.isPaused();
+ return Flags.enableAutoclickIndicator() && mAutoclickTypePanel.isPaused()
+ && !mAutoclickTypePanel.isHovered();
+ }
+
+ private void cancelPendingClick() {
+ if (mClickScheduler != null) {
+ mClickScheduler.cancel();
+ }
+ if (mAutoclickIndicatorScheduler != null) {
+ mAutoclickIndicatorScheduler.cancel();
+ }
}
@VisibleForTesting
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickLinearLayout.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickLinearLayout.java
new file mode 100644
index 000000000000..fe8adf75704d
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickLinearLayout.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2025 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.server.accessibility.autoclick;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+
+/**
+ * A custom LinearLayout that provides enhanced hover event handling.
+ * This class overrides hover methods to track hover events for the entire panel ViewGroup,
+ * including the descendant buttons. This allows for consistent hover behavior and feedback
+ * across the entire layout.
+ */
+public class AutoclickLinearLayout extends LinearLayout {
+ public interface OnHoverChangedListener {
+ /**
+ * Called when the hover state of the AutoclickLinearLayout changes.
+ *
+ * @param hovered {@code true} if the view is now hovered, {@code false} otherwise.
+ */
+ void onHoverChanged(boolean hovered);
+ }
+
+ private OnHoverChangedListener mListener;
+
+ public AutoclickLinearLayout(Context context) {
+ super(context);
+ }
+
+ public AutoclickLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AutoclickLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public AutoclickLinearLayout(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public void setOnHoverChangedListener(OnHoverChangedListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public boolean onInterceptHoverEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+ setHovered(action == MotionEvent.ACTION_HOVER_ENTER
+ || action == MotionEvent.ACTION_HOVER_MOVE);
+
+ return false;
+ }
+
+ @Override
+ public void onHoverChanged(boolean hovered) {
+ super.onHoverChanged(hovered);
+
+ if (mListener != null) {
+ mListener.onHoverChanged(hovered);
+ }
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
index ab4b3b13eece..57bbb4a7a0a7 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
@@ -110,11 +110,18 @@ public class AutoclickTypePanel {
* @param paused {@code true} to pause autoclick, {@code false} to resume.
*/
void toggleAutoclickPause(boolean paused);
+
+ /**
+ * Called when the hovered state of the panel changes.
+ *
+ * @param hovered {@code true} if the panel is now hovered, {@code false} otherwise.
+ */
+ void onHoverChange(boolean hovered);
}
private final Context mContext;
- private final View mContentView;
+ private final AutoclickLinearLayout mContentView;
private final WindowManager mWindowManager;
@@ -164,8 +171,9 @@ public class AutoclickTypePanel {
R.drawable.accessibility_autoclick_resume);
mContentView =
- LayoutInflater.from(context)
+ (AutoclickLinearLayout) LayoutInflater.from(context)
.inflate(R.layout.accessibility_autoclick_type_panel, null);
+ mContentView.setOnHoverChangedListener(mClickPanelController::onHoverChange);
mLeftClickButton =
mContentView.findViewById(R.id.accessibility_autoclick_left_click_layout);
mRightClickButton =
@@ -339,6 +347,10 @@ public class AutoclickTypePanel {
return mPaused;
}
+ public boolean isHovered() {
+ return mContentView.isHovered();
+ }
+
/** Toggles the panel expanded or collapsed state. */
private void togglePanelExpansion(@AutoclickType int clickType) {
final LinearLayout button = getButtonFromClickType(clickType);
@@ -520,7 +532,7 @@ public class AutoclickTypePanel {
@VisibleForTesting
@NonNull
- View getContentViewForTesting() {
+ AutoclickLinearLayout getContentViewForTesting() {
return mContentView;
}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
index 84402c85471f..12c35ae92cbe 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
@@ -177,6 +177,8 @@ abstract class DiscreteOpsRegistry {
*/
abstract void writeAndClearOldAccessHistory();
+ void shutdown() {}
+
/** Remove all discrete op events. */
abstract void clearHistory();
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
index 604cb30294a9..dc11be9aadb6 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
@@ -57,13 +57,18 @@ import java.util.Set;
public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
private static final String TAG = "DiscreteOpsSqlRegistry";
+ private static final long DB_WRITE_INTERVAL = Duration.ofMinutes(10).toMillis();
+ private static final long EXPIRED_ENTRY_DELETION_INTERVAL = Duration.ofHours(6).toMillis();
+
+ // Event type handled by SqliteWriteHandler
+ private static final int WRITE_DATABASE_RECURRING = 1;
+ private static final int DELETE_EXPIRED_ENTRIES = 2;
+ private static final int WRITE_DATABASE_CACHE_FULL = 3;
+
private final Context mContext;
private final DiscreteOpsDbHelper mDiscreteOpsDbHelper;
private final SqliteWriteHandler mSqliteWriteHandler;
private final DiscreteOpCache mDiscreteOpCache = new DiscreteOpCache(512);
- private static final long THREE_HOURS = Duration.ofHours(3).toMillis();
- private static final int WRITE_CACHE_EVICTED_OP_EVENTS = 1;
- private static final int DELETE_OLD_OP_EVENTS = 2;
// Attribution chain id is used to identify an attribution source chain, This is
// set for startOp only. PermissionManagerService resets this ID on device restart, so
// we use previously persisted chain id as offset, and add it to chain id received from
@@ -83,6 +88,9 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
mSqliteWriteHandler = new SqliteWriteHandler(thread.getLooper());
mDiscreteOpsDbHelper = new DiscreteOpsDbHelper(context, databaseFile);
mChainIdOffset = mDiscreteOpsDbHelper.getLargestAttributionChainId();
+ mSqliteWriteHandler.sendEmptyMessageDelayed(WRITE_DATABASE_RECURRING, DB_WRITE_INTERVAL);
+ mSqliteWriteHandler.sendEmptyMessageDelayed(DELETE_EXPIRED_ENTRIES,
+ EXPIRED_ENTRY_DELETION_INTERVAL);
}
@Override
@@ -117,15 +125,14 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
}
@Override
- void writeAndClearOldAccessHistory() {
- // Let the sql impl also follow the same disk write frequencies as xml,
- // controlled by AppOpsService.
+ void shutdown() {
+ mSqliteWriteHandler.removeAllPendingMessages();
mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.getAllEventsAndClear());
- if (!mSqliteWriteHandler.hasMessages(DELETE_OLD_OP_EVENTS)) {
- if (mSqliteWriteHandler.sendEmptyMessageDelayed(DELETE_OLD_OP_EVENTS, THREE_HOURS)) {
- Slog.w(TAG, "DELETE_OLD_OP_EVENTS is not queued");
- }
- }
+ }
+
+ @Override
+ void writeAndClearOldAccessHistory() {
+ // no-op
}
@Override
@@ -175,7 +182,7 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
@Nullable String attributionTagFilter, int opFlagsFilter,
Set<String> attributionExemptPkgs) {
// flush the cache into database before read.
- writeAndClearOldAccessHistory();
+ mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.getAllEventsAndClear());
boolean assembleChains = attributionExemptPkgs != null;
IntArray opCodes = getAppOpCodes(filter, opNamesFilter);
beginTimeMillis = Math.max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff,
@@ -363,20 +370,59 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case WRITE_CACHE_EVICTED_OP_EVENTS:
- List<DiscreteOp> opEvents = (List<DiscreteOp>) msg.obj;
- mDiscreteOpsDbHelper.insertDiscreteOps(opEvents);
- break;
- case DELETE_OLD_OP_EVENTS:
+ case WRITE_DATABASE_RECURRING -> {
+ try {
+ List<DiscreteOp> evictedEvents;
+ synchronized (mDiscreteOpCache) {
+ evictedEvents = mDiscreteOpCache.evict();
+ }
+ mDiscreteOpsDbHelper.insertDiscreteOps(evictedEvents);
+ } finally {
+ mSqliteWriteHandler.sendEmptyMessageDelayed(WRITE_DATABASE_RECURRING,
+ DB_WRITE_INTERVAL);
+ // Schedule a cleanup to truncate older (before cutoff time) entries.
+ if (!mSqliteWriteHandler.hasMessages(DELETE_EXPIRED_ENTRIES)) {
+ mSqliteWriteHandler.sendEmptyMessageDelayed(DELETE_EXPIRED_ENTRIES,
+ EXPIRED_ENTRY_DELETION_INTERVAL);
+ }
+ }
+ }
+ case DELETE_EXPIRED_ENTRIES -> {
long cutOffTimeStamp = System.currentTimeMillis() - sDiscreteHistoryCutoff;
mDiscreteOpsDbHelper.execSQL(
DiscreteOpsTable.DELETE_TABLE_DATA_BEFORE_ACCESS_TIME,
new Object[]{cutOffTimeStamp});
- break;
- default:
- throw new IllegalStateException("Unexpected value: " + msg.what);
+ }
+ case WRITE_DATABASE_CACHE_FULL -> {
+ try {
+ List<DiscreteOp> evictedEvents;
+ synchronized (mDiscreteOpCache) {
+ evictedEvents = mDiscreteOpCache.evict();
+ // if nothing to evict, just write the whole cache to database.
+ if (evictedEvents.isEmpty()
+ && mDiscreteOpCache.size() >= mDiscreteOpCache.capacity()) {
+ evictedEvents.addAll(mDiscreteOpCache.mCache);
+ mDiscreteOpCache.clear();
+ }
+ }
+ mDiscreteOpsDbHelper.insertDiscreteOps(evictedEvents);
+ } finally {
+ // Just in case initial message is not scheduled.
+ if (!mSqliteWriteHandler.hasMessages(WRITE_DATABASE_RECURRING)) {
+ mSqliteWriteHandler.sendEmptyMessageDelayed(WRITE_DATABASE_RECURRING,
+ DB_WRITE_INTERVAL);
+ }
+ }
+ }
+ default -> throw new IllegalStateException("Unexpected value: " + msg.what);
}
}
+
+ void removeAllPendingMessages() {
+ removeMessages(WRITE_DATABASE_RECURRING);
+ removeMessages(DELETE_EXPIRED_ENTRIES);
+ removeMessages(WRITE_DATABASE_CACHE_FULL);
+ }
}
/**
@@ -390,6 +436,7 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
* 4) During shutdown.
*/
class DiscreteOpCache {
+ private static final String TAG = "DiscreteOpCache";
private final int mCapacity;
private final ArraySet<DiscreteOp> mCache;
@@ -404,23 +451,9 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
return;
}
mCache.add(opEvent);
+
if (mCache.size() >= mCapacity) {
- if (DEBUG_LOG) {
- Slog.i(TAG, "Current discrete ops cache size: " + mCache.size());
- }
- List<DiscreteOp> evictedEvents = evict();
- if (DEBUG_LOG) {
- Slog.i(TAG, "Evicted discrete ops size: " + evictedEvents.size());
- }
- // if nothing to evict, just write the whole cache to disk
- if (evictedEvents.isEmpty()) {
- Slog.w(TAG, "No discrete ops event is evicted, write cache to db.");
- evictedEvents.addAll(mCache);
- mCache.clear();
- }
- Message msg = mSqliteWriteHandler.obtainMessage(
- WRITE_CACHE_EVICTED_OP_EVENTS, evictedEvents);
- mSqliteWriteHandler.sendMessage(msg);
+ mSqliteWriteHandler.sendEmptyMessage(WRITE_DATABASE_CACHE_FULL);
}
}
}
@@ -461,6 +494,14 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
}
}
+ int size() {
+ return mCache.size();
+ }
+
+ int capacity() {
+ return mCapacity;
+ }
+
/**
* Remove all entries from the cache.
*/
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 928a4b270b59..d267e0d9e536 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -750,6 +750,7 @@ final class HistoricalRegistry {
}
// Do not call persistPendingHistory inside the memory lock, due to possible deadlock
persistPendingHistory();
+ mDiscreteRegistry.shutdown();
}
void persistPendingHistory() {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index f51e60c101e4..36686fc086f1 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -312,7 +312,6 @@ public class BackgroundActivityStartController {
private final @ActivityManager.ProcessState int mCallingUidProcState;
private final boolean mIsCallingUidPersistentSystemProcess;
final BackgroundStartPrivileges mBalAllowedByPiSender;
- final BackgroundStartPrivileges mBalAllowedByPiCreatorWithHardening;
final BackgroundStartPrivileges mBalAllowedByPiCreator;
private final String mRealCallingPackage;
private final int mRealCallingUid;
@@ -379,22 +378,14 @@ public class BackgroundActivityStartController {
if (mAutoOptInCaller) {
// grant BAL privileges unless explicitly opted out
- mBalAllowedByPiCreatorWithHardening = mBalAllowedByPiCreator =
+ mBalAllowedByPiCreator =
callerBackgroundActivityStartMode == MODE_BACKGROUND_ACTIVITY_START_DENIED
? BackgroundStartPrivileges.NONE
: BackgroundStartPrivileges.ALLOW_BAL;
} else {
// for PendingIntents we restrict BAL based on target_sdk
- mBalAllowedByPiCreatorWithHardening = getBackgroundStartPrivilegesAllowedByCreator(
+ mBalAllowedByPiCreator = getBackgroundStartPrivilegesAllowedByCreator(
callingUid, callingPackage, checkedOptions);
- final BackgroundStartPrivileges mBalAllowedByPiCreatorWithoutHardening =
- callerBackgroundActivityStartMode
- == MODE_BACKGROUND_ACTIVITY_START_DENIED
- ? BackgroundStartPrivileges.NONE
- : BackgroundStartPrivileges.ALLOW_BAL;
- mBalAllowedByPiCreator = balRequireOptInByPendingIntentCreator()
- ? mBalAllowedByPiCreatorWithHardening
- : mBalAllowedByPiCreatorWithoutHardening;
}
if (mAutoOptInReason != null) {
@@ -585,9 +576,8 @@ public class BackgroundActivityStartController {
if (mCallerApp != null) {
sb.append("; inVisibleTask: ").append(mCallerApp.hasActivityInVisibleTask());
}
- sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
- sb.append("; balAllowedByPiCreatorWithHardening: ")
- .append(mBalAllowedByPiCreatorWithHardening);
+ sb.append("; balAllowedByPiCreator: ")
+ .append(mBalAllowedByPiCreator);
if (mResultForCaller != null) {
sb.append("; resultIfPiCreatorAllowsBal: ")
.append(balCodeToString(mResultForCaller.mCode));
@@ -638,14 +628,13 @@ public class BackgroundActivityStartController {
}
static class BalVerdict {
- static final BalVerdict BLOCK = new BalVerdict(BAL_BLOCK, false, "Blocked");
+ static final BalVerdict BLOCK = new BalVerdict(BAL_BLOCK, "Blocked");
static final BalVerdict ALLOW_BY_DEFAULT =
- new BalVerdict(BAL_ALLOW_DEFAULT, false, "Default");
+ new BalVerdict(BAL_ALLOW_DEFAULT, "Default");
// Careful using this - it will bypass all ASM checks.
static final BalVerdict ALLOW_PRIVILEGED =
- new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID, false, "PRIVILEGED");
+ new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID, "PRIVILEGED");
private final @BalCode int mCode;
- private final boolean mBackground;
private final String mMessage;
private String mProcessInfo;
// indicates BAL would be blocked because only creator of the PI has the privilege to allow
@@ -654,8 +643,7 @@ public class BackgroundActivityStartController {
/** indicates that this verdict is based on the real calling UID and not the calling UID */
private boolean mBasedOnRealCaller;
- BalVerdict(@BalCode int balCode, boolean background, String message) {
- this.mBackground = background;
+ BalVerdict(@BalCode int balCode, String message) {
this.mCode = balCode;
this.mMessage = message;
}
@@ -708,16 +696,7 @@ public class BackgroundActivityStartController {
builder.append(" [realCaller]");
}
if (DEBUG_ACTIVITY_STARTS) {
- builder.append(" (");
- if (mBackground) {
- builder.append("Background ");
- }
- builder.append("Activity start ");
- if (mCode == BAL_BLOCK) {
- builder.append("denied");
- } else {
- builder.append("allowed: ").append(mMessage);
- }
+ builder.append(" (").append(mMessage);
if (mProcessInfo != null) {
builder.append(" ");
builder.append(mProcessInfo);
@@ -795,7 +774,6 @@ public class BackgroundActivityStartController {
// to realCallingUid when calculating resultForRealCaller below.
if (getService().hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
state.setResultForRealCaller(new BalVerdict(BAL_ALLOW_SDK_SANDBOX,
- /*background*/ false,
"uid in SDK sandbox has visible (non-toast) window"));
return allowBasedOnRealCaller(state);
}
@@ -1059,8 +1037,7 @@ public class BackgroundActivityStartController {
|| state.mAppSwitchState == APP_SWITCH_FG_ONLY
|| isHomeApp(state.mCallingUid, state.mCallingPackage);
if (appSwitchAllowedOrFg && state.mCallingUidHasVisibleActivity) {
- return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
- /*background*/ false, "callingUid has visible window");
+ return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, "callingUid has visible window");
}
return BalVerdict.BLOCK;
};
@@ -1068,7 +1045,7 @@ public class BackgroundActivityStartController {
private final BalExemptionCheck mCheckCallerNonAppVisible = state -> {
if (state.mCallingUidHasNonAppVisibleWindow) {
return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
- /*background*/ false, "callingUid has non-app visible window "
+ "callingUid has non-app visible window "
+ getService().mActiveUids.getNonAppVisibleWindowDetails(state.mCallingUid));
}
return BalVerdict.BLOCK;
@@ -1080,9 +1057,7 @@ public class BackgroundActivityStartController {
if (state.mCallingUid == Process.ROOT_UID
|| callingAppId == Process.SYSTEM_UID
|| callingAppId == Process.NFC_UID) {
- return new BalVerdict(
- BAL_ALLOW_ALLOWLISTED_UID, /*background*/ false,
- "Important callingUid");
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID, "Important callingUid");
}
return BalVerdict.BLOCK;
};
@@ -1090,9 +1065,7 @@ public class BackgroundActivityStartController {
private final BalExemptionCheck mCheckCallerIsAllowlistedComponent = state -> {
// Always allow home application to start activities.
if (isHomeApp(state.mCallingUid, state.mCallingPackage)) {
- return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ false,
- "Home app");
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT, "Home app");
}
final int callingAppId = UserHandle.getAppId(state.mCallingUid);
@@ -1100,37 +1073,31 @@ public class BackgroundActivityStartController {
final WindowState imeWindow =
getService().mRootWindowContainer.getCurrentInputMethodWindow();
if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
- return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ false,
- "Active ime");
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT, "Active ime");
}
// don't abort if the callingUid is a persistent system process
if (state.mIsCallingUidPersistentSystemProcess) {
return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ false, "callingUid is persistent system process");
+ "callingUid is persistent system process");
}
// don't abort if the caller has the same uid as the recents component
if (getSupervisor().mRecentTasks.isCallerRecents(state.mCallingUid)) {
- return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ true, "Recents Component");
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT, "Recents Component");
}
// don't abort if the callingUid is the device owner
if (getService().isDeviceOwner(state.mCallingUid)) {
- return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ true, "Device Owner");
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT, "Device Owner");
}
// don't abort if the callingUid is a affiliated profile owner
if (getService().isAffiliatedProfileOwner(state.mCallingUid)) {
- return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ true, "Affiliated Profile Owner");
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT, "Affiliated Profile Owner");
}
// don't abort if the callingUid has companion device
final int callingUserId = UserHandle.getUserId(state.mCallingUid);
if (getService().isAssociatedCompanionApp(callingUserId, state.mCallingUid)) {
- return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ true, "Companion App");
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT, "Companion App");
}
return BalVerdict.BLOCK;
};
@@ -1139,7 +1106,6 @@ public class BackgroundActivityStartController {
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
if (hasBalPermission(state.mCallingUid, state.mCallingPid)) {
return new BalVerdict(BAL_ALLOW_PERMISSION,
- /*background*/ true,
"START_ACTIVITIES_FROM_BACKGROUND permission granted");
}
return BalVerdict.BLOCK;
@@ -1149,7 +1115,7 @@ public class BackgroundActivityStartController {
if (getService().hasSystemAlertWindowPermission(state.mCallingUid, state.mCallingPid,
state.mCallingPackage)) {
return new BalVerdict(BAL_ALLOW_SAW_PERMISSION,
- /*background*/ true, "SYSTEM_ALERT_WINDOW permission is granted");
+ "SYSTEM_ALERT_WINDOW permission is granted");
}
return BalVerdict.BLOCK;
};
@@ -1159,7 +1125,7 @@ public class BackgroundActivityStartController {
if (isSystemExemptFlagEnabled() && getService().getAppOpsManager().checkOpNoThrow(
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
state.mCallingUid, state.mCallingPackage) == AppOpsManager.MODE_ALLOWED) {
- return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
+ return new BalVerdict(BAL_ALLOW_PERMISSION,
"OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop is granted");
}
return BalVerdict.BLOCK;
@@ -1200,8 +1166,7 @@ public class BackgroundActivityStartController {
|| state.mAppSwitchState == APP_SWITCH_FG_ONLY
|| isHomeApp(state.mRealCallingUid, state.mRealCallingPackage);
if (appSwitchAllowedOrFg && state.mRealCallingUidHasVisibleActivity) {
- return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
- /*background*/ false, "realCallingUid has visible window");
+ return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, "realCallingUid has visible window");
}
return BalVerdict.BLOCK;
};
@@ -1209,9 +1174,9 @@ public class BackgroundActivityStartController {
private final BalExemptionCheck mCheckRealCallerNonAppVisible = state -> {
if (state.mRealCallingUidHasNonAppVisibleWindow) {
return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
- /*background*/ false, "realCallingUid has non-app visible window "
- + getService().mActiveUids.getNonAppVisibleWindowDetails(
- state.mRealCallingUid));
+ "realCallingUid has non-app visible window "
+ + getService().mActiveUids.getNonAppVisibleWindowDetails(
+ state.mRealCallingUid));
}
return BalVerdict.BLOCK;
};
@@ -1230,9 +1195,7 @@ public class BackgroundActivityStartController {
== MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
if (allowAlways
&& hasBalPermission(state.mRealCallingUid, state.mRealCallingPid)) {
- return new BalVerdict(BAL_ALLOW_PERMISSION,
- /*background*/ false,
- "realCallingUid has BAL permission.");
+ return new BalVerdict(BAL_ALLOW_PERMISSION, "realCallingUid has BAL permission.");
}
return BalVerdict.BLOCK;
};
@@ -1245,7 +1208,7 @@ public class BackgroundActivityStartController {
&& getService().hasSystemAlertWindowPermission(state.mRealCallingUid,
state.mRealCallingPid, state.mRealCallingPackage)) {
return new BalVerdict(BAL_ALLOW_SAW_PERMISSION,
- /*background*/ true, "SYSTEM_ALERT_WINDOW permission is granted");
+ "SYSTEM_ALERT_WINDOW permission is granted");
}
return BalVerdict.BLOCK;
};
@@ -1258,7 +1221,6 @@ public class BackgroundActivityStartController {
if ((allowAlways || state.mAllowBalExemptionForSystemProcess)
&& state.mIsRealCallingUidPersistentSystemProcess) {
return new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID,
- /*background*/ false,
"realCallingUid is persistent system process AND intent "
+ "sender forced to allow.");
}
@@ -1270,7 +1232,6 @@ public class BackgroundActivityStartController {
if (getService().isAssociatedCompanionApp(
UserHandle.getUserId(state.mRealCallingUid), state.mRealCallingUid)) {
return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ false,
"realCallingUid is a companion app.");
}
return BalVerdict.BLOCK;
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index ccf1aedb3177..31b239421baf 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -125,27 +125,27 @@ class BackgroundLaunchProcessController {
long lastActivityFinishTime) {
// Allow if the proc is instrumenting with background activity starts privs.
if (checkConfiguration.checkOtherExemptions && hasBackgroundActivityStartPrivileges) {
- return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
+ return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/
"process instrumenting with background activity starts privileges");
}
// Allow if the flag was explicitly set.
if (checkConfiguration.checkOtherExemptions && isBackgroundStartAllowedByToken(uid,
packageName, checkConfiguration.isCheckingForFgsStart)) {
return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_TOKEN : BAL_ALLOW_PERMISSION,
- /*background*/ true, "process allowed by token");
+ /*background*/ "process allowed by token");
}
// Allow if the caller is bound by a UID that's currently foreground.
// But still respect the appSwitchState.
if (checkConfiguration.checkVisibility && appSwitchState != APP_SWITCH_DISALLOW
&& isBoundByForegroundUid()) {
return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_BOUND_BY_FOREGROUND
- : BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false,
+ : BAL_ALLOW_VISIBLE_WINDOW, /*background*/
"process bound by foreground uid");
}
// Allow if the caller has an activity in any foreground task.
if (checkConfiguration.checkOtherExemptions && hasActivityInVisibleTask
&& appSwitchState != APP_SWITCH_DISALLOW) {
- return new BalVerdict(BAL_ALLOW_FOREGROUND, /*background*/ false,
+ return new BalVerdict(BAL_ALLOW_FOREGROUND, /*background*/
"process has activity in foreground task");
}
@@ -160,7 +160,7 @@ class BackgroundLaunchProcessController {
long timeSinceLastStartOrFinish = now - Math.max(lastActivityLaunchTime,
lastActivityFinishTime);
if (timeSinceLastStartOrFinish < checkConfiguration.gracePeriod) {
- return new BalVerdict(BAL_ALLOW_GRACE_PERIOD, /*background*/ true,
+ return new BalVerdict(BAL_ALLOW_GRACE_PERIOD, /*background*/
"within " + checkConfiguration.gracePeriod + "ms grace period ("
+ timeSinceLastStartOrFinish + "ms)");
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 64c19ff70c9f..574ab05075c4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6575,6 +6575,22 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
.getKeyguardController().isKeyguardLocked(mDisplayId);
}
+ boolean isKeyguardLockedOrAodShowing() {
+ return isKeyguardLocked() || isAodShowing();
+ }
+
+ /**
+ * @return whether aod is showing for this display
+ */
+ boolean isAodShowing() {
+ final boolean isAodShowing = mRootWindowContainer.mTaskSupervisor
+ .getKeyguardController().isAodShowing(mDisplayId);
+ if (mDisplayId == DEFAULT_DISPLAY && isAodShowing) {
+ return !isKeyguardGoingAway();
+ }
+ return isAodShowing;
+ }
+
/**
* @return whether keyguard is going away on this display
*/
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 6d73739e5046..4eeed5ec8423 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_FLAG_AOD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
@@ -216,6 +217,9 @@ class KeyguardController {
} else if (keyguardShowing && !state.mKeyguardShowing) {
transition.addFlag(TRANSIT_FLAG_KEYGUARD_APPEARING);
}
+ if (mWindowManager.mFlags.mAodTransition && aodShowing && !state.mAodShowing) {
+ transition.addFlag(TRANSIT_FLAG_AOD_APPEARING);
+ }
}
}
// Update the task snapshot if the screen will not be turned off. To make sure that the
@@ -238,19 +242,28 @@ class KeyguardController {
state.mAodShowing = aodShowing;
state.writeEventLog("setKeyguardShown");
- if (keyguardChanged) {
- // Irrelevant to AOD.
- state.mKeyguardGoingAway = false;
- if (keyguardShowing) {
- state.mDismissalRequested = false;
+ if (keyguardChanged || (mWindowManager.mFlags.mAodTransition && aodChanged)) {
+ if (keyguardChanged) {
+ // Irrelevant to AOD.
+ state.mKeyguardGoingAway = false;
+ if (keyguardShowing) {
+ state.mDismissalRequested = false;
+ }
}
if (goingAwayRemoved
- || (keyguardShowing && !Display.isOffState(dc.getDisplayInfo().state))) {
+ || (keyguardShowing && !Display.isOffState(dc.getDisplayInfo().state))
+ || (mWindowManager.mFlags.mAodTransition && aodShowing)) {
// Keyguard decided to show or stopped going away. Send a transition to animate back
// to the locked state before holding the sleep token again
if (!ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
- dc.requestTransitionAndLegacyPrepare(
- TRANSIT_TO_FRONT, TRANSIT_FLAG_KEYGUARD_APPEARING, /* trigger= */ null);
+ if (keyguardChanged) {
+ dc.requestTransitionAndLegacyPrepare(TRANSIT_TO_FRONT,
+ TRANSIT_FLAG_KEYGUARD_APPEARING, /* trigger= */ null);
+ }
+ if (mWindowManager.mFlags.mAodTransition && aodChanged && aodShowing) {
+ dc.requestTransitionAndLegacyPrepare(TRANSIT_TO_FRONT,
+ TRANSIT_FLAG_AOD_APPEARING, /* trigger= */ null);
+ }
}
dc.mWallpaperController.adjustWallpaperWindows();
dc.executeAppTransition();
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 3db1d50f3d6a..c78cdaa10df2 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -36,6 +36,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_AOD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -980,6 +981,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return false;
}
+ boolean isInAodAppearTransition() {
+ return (mFlags & TRANSIT_FLAG_AOD_APPEARING) != 0;
+ }
+
/**
* Specifies configuration change explicitly for the window container, so it can be chosen as
* transition target. This is usually used with transition mode
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 11c5c9345ab2..9b3b4451a746 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -526,6 +526,19 @@ class TransitionController {
return false;
}
+ boolean isInAodAppearTransition() {
+ if (mCollectingTransition != null && mCollectingTransition.isInAodAppearTransition()) {
+ return true;
+ }
+ for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
+ if (mWaitingTransitions.get(i).isInAodAppearTransition()) return true;
+ }
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ if (mPlayingTransitions.get(i).isInAodAppearTransition()) return true;
+ }
+ return false;
+ }
+
/**
* @return A pair of the transition and restore-behind target for the given {@param container}.
* @param container An ancestor of a transient-launch activity
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index a8b9fedcdc73..644417ec98e5 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -166,6 +166,14 @@ class WallpaperController {
mFindResults.setWallpaperTarget(w);
return false;
}
+ } else if (mService.mFlags.mAodTransition
+ && mDisplayContent.isKeyguardLockedOrAodShowing()) {
+ if (mService.mPolicy.isKeyguardHostWindow(w.mAttrs)
+ && w.mTransitionController.isInAodAppearTransition()) {
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Found aod transition wallpaper target: " + w);
+ mFindResults.setWallpaperTarget(w);
+ return true;
+ }
}
final boolean animationWallpaper = animatingContainer != null
@@ -678,7 +686,8 @@ class WallpaperController {
private WallpaperWindowToken getTokenForTarget(WindowState target) {
if (target == null) return null;
WindowState window = mFindResults.getTopWallpaper(
- target.canShowWhenLocked() && mService.isKeyguardLocked());
+ (target.canShowWhenLocked() && mService.isKeyguardLocked())
+ || (mService.mFlags.mAodTransition && mDisplayContent.isAodShowing()));
return window == null ? null : window.mToken.asWallpaperToken();
}
@@ -721,7 +730,9 @@ class WallpaperController {
if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) {
mFindResults.setWallpaperTarget(
- mFindResults.getTopWallpaper(mDisplayContent.isKeyguardLocked()));
+ mFindResults.getTopWallpaper(mService.mFlags.mAodTransition
+ ? mDisplayContent.isKeyguardLockedOrAodShowing()
+ : mDisplayContent.isKeyguardLocked()));
}
}
@@ -885,11 +896,17 @@ class WallpaperController {
if (mDisplayContent.mWmService.mFlags.mEnsureWallpaperInTransitions) {
visibleRequested = mWallpaperTarget != null && mWallpaperTarget.isVisibleRequested();
}
- updateWallpaperTokens(visibleRequested, mDisplayContent.isKeyguardLocked());
+ updateWallpaperTokens(visibleRequested,
+ mService.mFlags.mAodTransition
+ ? mDisplayContent.isKeyguardLockedOrAodShowing()
+ : mDisplayContent.isKeyguardLocked());
ProtoLog.v(WM_DEBUG_WALLPAPER,
"Wallpaper at display %d - visibility: %b, keyguardLocked: %b",
- mDisplayContent.getDisplayId(), visible, mDisplayContent.isKeyguardLocked());
+ mDisplayContent.getDisplayId(), visible,
+ mService.mFlags.mAodTransition
+ ? mDisplayContent.isKeyguardLockedOrAodShowing()
+ : mDisplayContent.isKeyguardLocked());
if (visible && mLastFrozen != mFindResults.isWallpaperTargetForLetterbox) {
mLastFrozen = mFindResults.isWallpaperTargetForLetterbox;
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java
index 6ad3df1dd6f2..ac11216bdf0a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java
@@ -102,11 +102,12 @@ import java.io.FileInputStream;
import java.io.IOException;
/**
- * Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest}
+ * Run as {@code atest
+ * FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceMockedTest}
*/
-public final class UserManagerServiceTest {
+public final class UserManagerServiceMockedTest {
- private static final String TAG = UserManagerServiceTest.class.getSimpleName();
+ private static final String TAG = UserManagerServiceMockedTest.class.getSimpleName();
/**
* Id for a simple user (that doesn't have profiles).
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index 17d8882b487c..ea83825cd810 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -571,6 +571,95 @@ public class AutoclickControllerTest {
assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isNotEqualTo(-1);
}
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void pauseButton_panelNotHovered_clickNotTriggeredWhenPaused() {
+ injectFakeMouseActionHoverMoveEvent();
+
+ // Pause autoclick and ensure the panel is not hovered.
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ when(mockAutoclickTypePanel.isPaused()).thenReturn(true);
+ when(mockAutoclickTypePanel.isHovered()).thenReturn(false);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ MotionEvent hoverMove = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 100,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30f,
+ /* y= */ 0f,
+ /* metaState= */ 0);
+ hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+
+ // Verify click is not triggered.
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isFalse();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isEqualTo(-1);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void pauseButton_panelHovered_clickTriggeredWhenPaused() {
+ injectFakeMouseActionHoverMoveEvent();
+
+ // Pause autoclick and hover the panel.
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ when(mockAutoclickTypePanel.isPaused()).thenReturn(true);
+ when(mockAutoclickTypePanel.isHovered()).thenReturn(true);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ MotionEvent hoverMove = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 100,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30f,
+ /* y= */ 0f,
+ /* metaState= */ 0);
+ hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+
+ // Verify click is triggered.
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isTrue();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isNotEqualTo(-1);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void pauseButton_unhoveringCancelsClickWhenPaused() {
+ injectFakeMouseActionHoverMoveEvent();
+
+ // Pause autoclick and hover the panel.
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ when(mockAutoclickTypePanel.isPaused()).thenReturn(true);
+ when(mockAutoclickTypePanel.isHovered()).thenReturn(true);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ MotionEvent hoverMove = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 100,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30f,
+ /* y= */ 0f,
+ /* metaState= */ 0);
+ hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+
+ // Verify click is triggered.
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isTrue();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isNotEqualTo(-1);
+
+ // Now simulate the pointer being moved outside the panel.
+ when(mockAutoclickTypePanel.isHovered()).thenReturn(false);
+ mController.clickPanelController.onHoverChange(/* hovered= */ false);
+
+ // Verify pending click is canceled.
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isFalse();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isEqualTo(-1);
+ }
+
private void injectFakeMouseActionHoverMoveEvent() {
MotionEvent event = getFakeMotionHoverMoveEvent();
event.setSource(InputDevice.SOURCE_MOUSE);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickLinearLayoutTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickLinearLayoutTest.java
new file mode 100644
index 000000000000..9e629f7c87a2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickLinearLayoutTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2025 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.server.accessibility.autoclick;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.view.MotionEvent;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Test cases for {@link AutoclickLinearLayout}. */
+@RunWith(AndroidTestingRunner.class)
+public class AutoclickLinearLayoutTest {
+ private boolean mHovered;
+
+ private final AutoclickLinearLayout.OnHoverChangedListener mListener =
+ new AutoclickLinearLayout.OnHoverChangedListener() {
+ @Override
+ public void onHoverChanged(boolean hovered) {
+ mHovered = hovered;
+ }
+ };
+
+ @Rule
+ public TestableContext mTestableContext =
+ new TestableContext(getInstrumentation().getContext());
+ private AutoclickLinearLayout mAutoclickLinearLayout;
+
+ @Before
+ public void setUp() {
+ mAutoclickLinearLayout = new AutoclickLinearLayout(mTestableContext);
+ }
+
+ @Test
+ public void autoclickLinearLayout_hoverChangedListener_setHovered() {
+ mHovered = false;
+ mAutoclickLinearLayout.setOnHoverChangedListener(mListener);
+ mAutoclickLinearLayout.onHoverChanged(/* hovered= */ true);
+ assertThat(mHovered).isTrue();
+ }
+
+ @Test
+ public void autoclickLinearLayout_hoverChangedListener_setNotHovered() {
+ mHovered = true;
+
+ mAutoclickLinearLayout.setOnHoverChangedListener(mListener);
+ mAutoclickLinearLayout.onHoverChanged(/* hovered= */ false);
+ assertThat(mHovered).isFalse();
+ }
+
+ @Test
+ public void autoclickLinearLayout_onInterceptHoverEvent_hovered() {
+ mAutoclickLinearLayout.setHovered(false);
+ mAutoclickLinearLayout.onInterceptHoverEvent(
+ getFakeMotionEvent(MotionEvent.ACTION_HOVER_ENTER));
+ assertThat(mAutoclickLinearLayout.isHovered()).isTrue();
+
+ mAutoclickLinearLayout.setHovered(false);
+ mAutoclickLinearLayout.onInterceptHoverEvent(
+ getFakeMotionEvent(MotionEvent.ACTION_HOVER_MOVE));
+ assertThat(mAutoclickLinearLayout.isHovered()).isTrue();
+ }
+
+ @Test
+ public void autoclickLinearLayout_onInterceptHoverEvent_hoveredExit() {
+ mAutoclickLinearLayout.setHovered(true);
+ mAutoclickLinearLayout.onInterceptHoverEvent(
+ getFakeMotionEvent(MotionEvent.ACTION_HOVER_EXIT));
+ assertThat(mAutoclickLinearLayout.isHovered()).isFalse();
+ }
+
+ private MotionEvent getFakeMotionEvent(int motionEventAction) {
+ return MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ /* action= */ motionEventAction,
+ /* x= */ 0,
+ /* y= */ 0,
+ /* metaState= */ 0);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
index 9e123406dff5..f7b16c808c50 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
@@ -78,6 +78,7 @@ public class AutoclickTypePanelTest {
private @AutoclickType int mActiveClickType = AUTOCLICK_TYPE_LEFT_CLICK;
private boolean mPaused;
+ private boolean mHovered;
private final ClickPanelControllerInterface clickPanelController =
new ClickPanelControllerInterface() {
@@ -90,6 +91,11 @@ public class AutoclickTypePanelTest {
public void toggleAutoclickPause(boolean paused) {
mPaused = paused;
}
+
+ @Override
+ public void onHoverChange(boolean hovered) {
+ mHovered = hovered;
+ }
};
@Before
@@ -412,6 +418,33 @@ public class AutoclickTypePanelTest {
upEvent.recycle();
}
+ @Test
+ public void hovered_IsHovered() {
+ AutoclickLinearLayout mContext = mAutoclickTypePanel.getContentViewForTesting();
+
+ assertThat(mAutoclickTypePanel.isHovered()).isFalse();
+ mContext.onInterceptHoverEvent(getFakeMotionHoverMoveEvent());
+ assertThat(mAutoclickTypePanel.isHovered()).isTrue();
+ }
+
+ @Test
+ public void hovered_OnHoverChange_isHovered() {
+ AutoclickLinearLayout mContext = mAutoclickTypePanel.getContentViewForTesting();
+
+ mHovered = false;
+ mContext.onHoverChanged(true);
+ assertThat(mHovered).isTrue();
+ }
+
+ @Test
+ public void hovered_OnHoverChange_isNotHovered() {
+ AutoclickLinearLayout mContext = mAutoclickTypePanel.getContentViewForTesting();
+
+ mHovered = true;
+ mContext.onHoverChanged(false);
+ assertThat(mHovered).isFalse();
+ }
+
private void verifyButtonHasSelectedStyle(@NonNull LinearLayout button) {
GradientDrawable gradientDrawable = (GradientDrawable) button.getBackground();
assertThat(gradientDrawable.getColor().getDefaultColor())
@@ -426,4 +459,14 @@ public class AutoclickTypePanelTest {
assertThat(params.x).isEqualTo(expectedPosition[2]);
assertThat(params.y).isEqualTo(expectedPosition[3]);
}
+
+ private MotionEvent getFakeMotionHoverMoveEvent() {
+ return MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 0,
+ /* y= */ 0,
+ /* metaState= */ 0);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java
index 84713079c9d3..01fee7f66497 100644
--- a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java
@@ -226,9 +226,9 @@ public class DiscreteAppOpSqlPersistenceTest {
mDiscreteRegistry.recordDiscreteAccess(event2);
}
- /** This clears in-memory cache and push records into the database. */
private void flushDiscreteOpsToDatabase() {
- mDiscreteRegistry.writeAndClearOldAccessHistory();
+ // This clears in-memory cache and push records from cache into the database.
+ mDiscreteRegistry.shutdown();
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
index 21cc3bac3938..8eea1c73d4f2 100644
--- a/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
@@ -106,7 +106,8 @@ public class DiscreteOpsMigrationAndRollbackTest {
opEvent.getDuration(), opEvent.getAttributionFlags(),
(int) opEvent.getChainId(), DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP);
}
- sqlRegistry.writeAndClearOldAccessHistory();
+ // flush records from cache to the database.
+ sqlRegistry.shutdown();
assertThat(sqlRegistry.getAllDiscreteOps().size()).isEqualTo(RECORD_COUNT);
assertThat(sqlRegistry.getLargestAttributionChainId()).isEqualTo(RECORD_COUNT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
index c934c55dfb8e..e3a8776aca6a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -486,7 +486,7 @@ public class BackgroundActivityStartControllerExemptionTests {
// setup state
when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
- new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
+ new BalVerdict(BAL_ALLOW_FOREGROUND, "allowed"));
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
// prepare call
@@ -523,7 +523,7 @@ public class BackgroundActivityStartControllerExemptionTests {
mCallerApp);
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
- new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
+ new BalVerdict(BAL_ALLOW_FOREGROUND, "allowed"));
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
@@ -572,7 +572,7 @@ public class BackgroundActivityStartControllerExemptionTests {
when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
BalVerdict.BLOCK);
when(otherProcess.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
- new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
+ new BalVerdict(BAL_ALLOW_FOREGROUND, "allowed"));
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
index 99e730ae76cf..cd5f3912bfc6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
@@ -92,7 +92,7 @@ public class BackgroundActivityStartControllerLogTests {
@Test
public void intent_visible_noLog() {
useIntent();
- BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false, "visible");
+ BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, "visible");
mState.setResultForCaller(finalVerdict);
mState.setResultForRealCaller(BalVerdict.BLOCK);
assertThat(mController.shouldLogStats(finalVerdict, mState)).isFalse();
@@ -101,7 +101,7 @@ public class BackgroundActivityStartControllerLogTests {
@Test
public void intent_saw_log() {
useIntent();
- BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION, false, "SAW");
+ BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION, "SAW");
mState.setResultForCaller(finalVerdict);
mState.setResultForRealCaller(BalVerdict.BLOCK);
assertThat(mController.shouldLogStats(finalVerdict, mState)).isTrue();
@@ -111,7 +111,7 @@ public class BackgroundActivityStartControllerLogTests {
@Test
public void pendingIntent_callerOnly_saw_log() {
usePendingIntent();
- BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION, false, "SAW");
+ BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION, "SAW");
mState.setResultForCaller(finalVerdict);
mState.setResultForRealCaller(BalVerdict.BLOCK);
assertThat(mController.shouldLogStats(finalVerdict, mState)).isTrue();
@@ -121,7 +121,7 @@ public class BackgroundActivityStartControllerLogTests {
@Test
public void pendingIntent_realCallerOnly_saw_log() {
usePendingIntent();
- BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION, false, "SAW")
+ BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION, "SAW")
.setBasedOnRealCaller();
mState.setResultForCaller(BalVerdict.BLOCK);
mState.setResultForRealCaller(finalVerdict);
@@ -131,7 +131,7 @@ public class BackgroundActivityStartControllerLogTests {
@Test
public void intent_shouldLogIntentActivity() {
- BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION, false, "SAW");
+ BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION, "SAW");
useIntent(APP1_UID);
assertThat(mController.shouldLogIntentActivity(finalVerdict, mState)).isFalse();
useIntent(SYSTEM_UID);
@@ -140,7 +140,7 @@ public class BackgroundActivityStartControllerLogTests {
@Test
public void pendingIntent_shouldLogIntentActivityForCaller() {
- BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION, false, "SAW");
+ BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION, "SAW");
usePendingIntent(APP1_UID, APP2_UID);
assertThat(mController.shouldLogIntentActivity(finalVerdict, mState)).isFalse();
usePendingIntent(SYSTEM_UID, SYSTEM_UID);
@@ -153,7 +153,7 @@ public class BackgroundActivityStartControllerLogTests {
@Test
public void pendingIntent_shouldLogIntentActivityForRealCaller() {
- BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION, false,
+ BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_SAW_PERMISSION,
"SAW").setBasedOnRealCaller();
usePendingIntent(APP1_UID, APP2_UID);
assertThat(mController.shouldLogIntentActivity(finalVerdict, mState)).isFalse();
@@ -168,7 +168,7 @@ public class BackgroundActivityStartControllerLogTests {
@Test
public void pendingIntent_realCallerOnly_visible_noLog() {
usePendingIntent();
- BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false,
+ BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
"visible").setBasedOnRealCaller();
mState.setResultForCaller(BalVerdict.BLOCK);
mState.setResultForRealCaller(finalVerdict);
@@ -178,7 +178,7 @@ public class BackgroundActivityStartControllerLogTests {
@Test
public void pendingIntent_callerOnly_visible_noLog() {
usePendingIntent();
- BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false, "visible");
+ BalVerdict finalVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, "visible");
mState.setResultForCaller(finalVerdict);
mState.setResultForRealCaller(BalVerdict.BLOCK);
assertThat(mController.shouldLogStats(finalVerdict, mState)).isTrue();
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index 51706d72cb35..fe9a6e746513 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -305,7 +305,7 @@ public class BackgroundActivityStartControllerTests {
@Test
public void testRegularActivityStart_allowedByCaller_isAllowed() {
// setup state
- BalVerdict callerVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false,
+ BalVerdict callerVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
"CallerIsVisible");
mController.setCallerVerdict(callerVerdict);
mController.setRealCallerVerdict(BalVerdict.BLOCK);
@@ -340,7 +340,7 @@ public class BackgroundActivityStartControllerTests {
@Test
public void testRegularActivityStart_allowedByRealCaller_isAllowed() {
// setup state
- BalVerdict realCallerVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false,
+ BalVerdict realCallerVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
"RealCallerIsVisible");
mController.setCallerVerdict(BalVerdict.BLOCK);
mController.setRealCallerVerdict(realCallerVerdict);
@@ -373,9 +373,9 @@ public class BackgroundActivityStartControllerTests {
public void testRegularActivityStart_allowedByCallerAndRealCaller_returnsCallerVerdict() {
// setup state
BalVerdict callerVerdict =
- new BalVerdict(BAL_ALLOW_PERMISSION, false, "CallerHasPermission");
+ new BalVerdict(BAL_ALLOW_PERMISSION, "CallerHasPermission");
BalVerdict realCallerVerdict =
- new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false, "RealCallerIsVisible");
+ new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, "RealCallerIsVisible");
mController.setCallerVerdict(callerVerdict);
mController.setRealCallerVerdict(realCallerVerdict);
@@ -411,9 +411,9 @@ public class BackgroundActivityStartControllerTests {
public void testPendingIntent_allowedByCallerAndRealCallerButOptOut_isBlocked() {
// setup state
BalVerdict callerVerdict =
- new BalVerdict(BAL_ALLOW_PERMISSION, false, "CallerhasPermission");
+ new BalVerdict(BAL_ALLOW_PERMISSION, "CallerhasPermission");
BalVerdict realCallerVerdict =
- new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false, "RealCallerIsVisible");
+ new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, "RealCallerIsVisible");
mController.setCallerVerdict(callerVerdict);
mController.setRealCallerVerdict(realCallerVerdict);
@@ -452,7 +452,7 @@ public class BackgroundActivityStartControllerTests {
public void testPendingIntent_allowedByCallerAndOptIn_isAllowed() {
// setup state
BalVerdict callerVerdict =
- new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false, "CallerIsVisible");
+ new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, "CallerIsVisible");
mController.setCallerVerdict(callerVerdict);
mController.setRealCallerVerdict(BalVerdict.BLOCK);
@@ -489,7 +489,7 @@ public class BackgroundActivityStartControllerTests {
public void testPendingIntent_allowedByRealCallerAndOptIn_isAllowed() {
// setup state
BalVerdict realCallerVerdict =
- new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false, "RealCallerIsVisible");
+ new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, "RealCallerIsVisible");
mController.setCallerVerdict(BalVerdict.BLOCK);
mController.setRealCallerVerdict(realCallerVerdict);
@@ -571,7 +571,6 @@ public class BackgroundActivityStartControllerTests {
+ "callerApp: mCallerApp; "
+ "inVisibleTask: false; "
+ "balAllowedByPiCreator: BSP.ALLOW_BAL; "
- + "balAllowedByPiCreatorWithHardening: BSP.ALLOW_BAL; "
+ "callerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
+ "hasRealCaller: true; "
+ "isCallForResult: false; "
@@ -674,7 +673,6 @@ public class BackgroundActivityStartControllerTests {
+ "callerApp: mCallerApp; "
+ "inVisibleTask: false; "
+ "balAllowedByPiCreator: BSP.NONE; "
- + "balAllowedByPiCreatorWithHardening: BSP.NONE; "
+ "callerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
+ "hasRealCaller: true; "
+ "isCallForResult: false; "
diff --git a/tools/codegen/OWNERS b/tools/codegen/OWNERS
index c9bd260ca7ae..e69de29bb2d1 100644
--- a/tools/codegen/OWNERS
+++ b/tools/codegen/OWNERS
@@ -1 +0,0 @@
-chiuwinson@google.com