summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tracy Zhou <tracyzhou@google.com> 2025-01-09 00:34:04 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-01-09 00:34:04 -0800
commit3e87c990b04ec431f1ff9c67dbbf549ec74fe2ab (patch)
tree39b35e789901a70a2d2b910569d6acc22549f076
parent23c308ada8330b3c8b2c22e75550172f7b200885 (diff)
parent635439ac7e08875c41ac61a91a477488118110ca (diff)
Merge "Introduce new shell module for app zoom out (display area approach)" into main
-rw-r--r--core/java/android/window/DisplayAreaOrganizer.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOut.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java158
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutDisplayAreaOrganizer.java157
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/appzoomout/AppZoomOutControllerTest.java87
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt11
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicy.java8
13 files changed, 513 insertions, 2 deletions
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 84ce247264f6..bd711fc79083 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -121,6 +121,14 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
public static final int FEATURE_WINDOWING_LAYER = FEATURE_SYSTEM_FIRST + 9;
/**
+ * Display area for rendering app zoom out. When there are multiple layers on the screen,
+ * we want to render these layers based on a depth model. Here we zoom out the layer behind,
+ * whether it's an app or the homescreen.
+ * @hide
+ */
+ public static final int FEATURE_APP_ZOOM_OUT = FEATURE_SYSTEM_FIRST + 10;
+
+ /**
* The last boundary of display area for system features
*/
public static final int FEATURE_SYSTEM_LAST = 10_000;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOut.java b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOut.java
new file mode 100644
index 000000000000..9451374befe0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOut.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 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.wm.shell.appzoomout;
+
+import com.android.wm.shell.shared.annotations.ExternalThread;
+
+/**
+ * Interface to engage with the app zoom out feature.
+ */
+@ExternalThread
+public interface AppZoomOut {
+
+ /**
+ * Called when the zoom out progress is updated, which is used to scale down the current app
+ * surface from fullscreen to the max pushback level we want to apply. {@param progress} ranges
+ * between [0,1], 0 when fullscreen, 1 when it's at the max pushback level.
+ */
+ void setProgress(float progress);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
new file mode 100644
index 000000000000..8cd7b0f48003
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 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.wm.shell.appzoomout;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.app.ActivityManager;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.util.Slog;
+import android.window.DisplayAreaInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.RemoteCallable;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.shared.annotations.ExternalThread;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.sysui.ShellInit;
+
+/** Class that manages the app zoom out UI and states. */
+public class AppZoomOutController implements RemoteCallable<AppZoomOutController>,
+ ShellTaskOrganizer.FocusListener, DisplayChangeController.OnDisplayChangingListener {
+
+ private static final String TAG = "AppZoomOutController";
+
+ private final Context mContext;
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final DisplayController mDisplayController;
+ private final AppZoomOutDisplayAreaOrganizer mDisplayAreaOrganizer;
+ private final ShellExecutor mMainExecutor;
+ private final AppZoomOutImpl mImpl = new AppZoomOutImpl();
+
+ private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
+ new DisplayController.OnDisplaysChangedListener() {
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ updateDisplayLayout(displayId);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ updateDisplayLayout(displayId);
+ }
+ };
+
+
+ public static AppZoomOutController create(Context context, ShellInit shellInit,
+ ShellTaskOrganizer shellTaskOrganizer, DisplayController displayController,
+ DisplayLayout displayLayout, @ShellMainThread ShellExecutor mainExecutor) {
+ AppZoomOutDisplayAreaOrganizer displayAreaOrganizer = new AppZoomOutDisplayAreaOrganizer(
+ context, displayLayout, mainExecutor);
+ return new AppZoomOutController(context, shellInit, shellTaskOrganizer, displayController,
+ displayAreaOrganizer, mainExecutor);
+ }
+
+ @VisibleForTesting
+ AppZoomOutController(Context context, ShellInit shellInit,
+ ShellTaskOrganizer shellTaskOrganizer, DisplayController displayController,
+ AppZoomOutDisplayAreaOrganizer displayAreaOrganizer,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ mContext = context;
+ mTaskOrganizer = shellTaskOrganizer;
+ mDisplayController = displayController;
+ mDisplayAreaOrganizer = displayAreaOrganizer;
+ mMainExecutor = mainExecutor;
+
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mTaskOrganizer.addFocusListener(this);
+
+ mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
+ mDisplayController.addDisplayChangingController(this);
+
+ mDisplayAreaOrganizer.registerOrganizer();
+ }
+
+ public AppZoomOut asAppZoomOut() {
+ return mImpl;
+ }
+
+ public void setProgress(float progress) {
+ mDisplayAreaOrganizer.setProgress(progress);
+ }
+
+ void updateDisplayLayout(int displayId) {
+ final DisplayLayout newDisplayLayout = mDisplayController.getDisplayLayout(displayId);
+ if (newDisplayLayout == null) {
+ Slog.w(TAG, "Failed to get new DisplayLayout.");
+ return;
+ }
+ mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
+ }
+
+ @Override
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (taskInfo == null) {
+ return;
+ }
+ if (taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_HOME) {
+ mDisplayAreaOrganizer.setIsHomeTaskFocused(taskInfo.isFocused);
+ }
+ }
+
+ @Override
+ public void onDisplayChange(int displayId, int fromRotation, int toRotation,
+ @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
+ // TODO: verify if there is synchronization issues.
+ mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation);
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mMainExecutor;
+ }
+
+ @ExternalThread
+ private class AppZoomOutImpl implements AppZoomOut {
+ @Override
+ public void setProgress(float progress) {
+ mMainExecutor.execute(() -> AppZoomOutController.this.setProgress(progress));
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutDisplayAreaOrganizer.java
new file mode 100644
index 000000000000..1c37461b2d2b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutDisplayAreaOrganizer.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 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.wm.shell.appzoomout;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.view.SurfaceControl;
+import android.window.DisplayAreaAppearedInfo;
+import android.window.DisplayAreaInfo;
+import android.window.DisplayAreaOrganizer;
+import android.window.WindowContainerToken;
+
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.wm.shell.common.DisplayLayout;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/** Display area organizer that manages the app zoom out UI and states. */
+public class AppZoomOutDisplayAreaOrganizer extends DisplayAreaOrganizer {
+
+ private static final float PUSHBACK_SCALE_FOR_LAUNCHER = 0.05f;
+ private static final float PUSHBACK_SCALE_FOR_APP = 0.025f;
+ private static final float INVALID_PROGRESS = -1;
+
+ private final DisplayLayout mDisplayLayout = new DisplayLayout();
+ private final Context mContext;
+ private final float mCornerRadius;
+ private final Map<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap =
+ new ArrayMap<>();
+
+ private float mProgress = INVALID_PROGRESS;
+ // Denote whether the home task is focused, null when it's not yet initialized.
+ @Nullable private Boolean mIsHomeTaskFocused;
+
+ public AppZoomOutDisplayAreaOrganizer(Context context,
+ DisplayLayout displayLayout, Executor mainExecutor) {
+ super(mainExecutor);
+ mContext = context;
+ mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
+ setDisplayLayout(displayLayout);
+ }
+
+ @Override
+ public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo, SurfaceControl leash) {
+ leash.setUnreleasedWarningCallSite(
+ "AppZoomOutDisplayAreaOrganizer.onDisplayAreaAppeared");
+ mDisplayAreaTokenMap.put(displayAreaInfo.token, leash);
+ }
+
+ @Override
+ public void onDisplayAreaVanished(DisplayAreaInfo displayAreaInfo) {
+ final SurfaceControl leash = mDisplayAreaTokenMap.get(displayAreaInfo.token);
+ if (leash != null) {
+ leash.release();
+ }
+ mDisplayAreaTokenMap.remove(displayAreaInfo.token);
+ }
+
+ public void registerOrganizer() {
+ final List<DisplayAreaAppearedInfo> displayAreaInfos = registerOrganizer(
+ AppZoomOutDisplayAreaOrganizer.FEATURE_APP_ZOOM_OUT);
+ for (int i = 0; i < displayAreaInfos.size(); i++) {
+ final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
+ onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
+ }
+ }
+
+ @Override
+ public void unregisterOrganizer() {
+ super.unregisterOrganizer();
+ reset();
+ }
+
+ void setProgress(float progress) {
+ if (mProgress == progress) {
+ return;
+ }
+
+ mProgress = progress;
+ apply();
+ }
+
+ void setIsHomeTaskFocused(boolean isHomeTaskFocused) {
+ if (mIsHomeTaskFocused != null && mIsHomeTaskFocused == isHomeTaskFocused) {
+ return;
+ }
+
+ mIsHomeTaskFocused = isHomeTaskFocused;
+ apply();
+ }
+
+ private void apply() {
+ if (mIsHomeTaskFocused == null || mProgress == INVALID_PROGRESS) {
+ return;
+ }
+
+ SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+ float scale = mProgress * (mIsHomeTaskFocused
+ ? PUSHBACK_SCALE_FOR_LAUNCHER : PUSHBACK_SCALE_FOR_APP);
+ mDisplayAreaTokenMap.forEach((token, leash) -> updateSurface(tx, leash, scale));
+ tx.apply();
+ }
+
+ void setDisplayLayout(DisplayLayout displayLayout) {
+ mDisplayLayout.set(displayLayout);
+ }
+
+ private void reset() {
+ setProgress(0);
+ mProgress = INVALID_PROGRESS;
+ mIsHomeTaskFocused = null;
+ }
+
+ private void updateSurface(SurfaceControl.Transaction tx, SurfaceControl leash, float scale) {
+ if (scale == 0) {
+ // Reset when scale is set back to 0.
+ tx
+ .setCrop(leash, null)
+ .setScale(leash, 1, 1)
+ .setPosition(leash, 0, 0)
+ .setCornerRadius(leash, 0);
+ return;
+ }
+
+ tx
+ // Rounded corner can only be applied if a crop is set.
+ .setCrop(leash, 0, 0, mDisplayLayout.width(), mDisplayLayout.height())
+ .setScale(leash, 1 - scale, 1 - scale)
+ .setPosition(leash, scale * mDisplayLayout.width() * 0.5f,
+ scale * mDisplayLayout.height() * 0.5f)
+ .setCornerRadius(leash, mCornerRadius * (1 - scale));
+ }
+
+ void onRotateDisplay(Context context, int toRotation) {
+ if (mDisplayLayout.rotation() == toRotation) {
+ return;
+ }
+ mDisplayLayout.rotateTo(context.getResources(), toRotation);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java
index c493aadd57b0..151dc438702d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMComponent.java
@@ -20,6 +20,7 @@ import android.os.HandlerThread;
import androidx.annotation.Nullable;
+import com.android.wm.shell.appzoomout.AppZoomOut;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.desktopmode.DesktopMode;
@@ -112,4 +113,7 @@ public interface WMComponent {
*/
@WMSingleton
Optional<DesktopMode> getDesktopMode();
+
+ @WMSingleton
+ Optional<AppZoomOut> getAppZoomOut();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 090d1444dbef..ab3c33ec7e43 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -112,6 +112,8 @@ import com.android.wm.shell.shared.annotations.ShellAnimationThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.annotations.ShellSplashscreenThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.appzoomout.AppZoomOut;
+import com.android.wm.shell.appzoomout.AppZoomOutController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingSurface;
@@ -1051,6 +1053,20 @@ public abstract class WMShellBaseModule {
}
//
+ // App zoom out (optional feature)
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<AppZoomOut> provideAppZoomOut(
+ Optional<AppZoomOutController> appZoomOutController) {
+ return appZoomOutController.map((controller) -> controller.asAppZoomOut());
+ }
+
+ @BindsOptionalOf
+ abstract AppZoomOutController optionalAppZoomOutController();
+
+ //
// Task Stack
//
@@ -1094,6 +1110,7 @@ public abstract class WMShellBaseModule {
Optional<RecentTasksController> recentTasksOptional,
Optional<RecentsTransitionHandler> recentsTransitionHandlerOptional,
Optional<OneHandedController> oneHandedControllerOptional,
+ Optional<AppZoomOutController> appZoomOutControllerOptional,
Optional<HideDisplayCutoutController> hideDisplayCutoutControllerOptional,
Optional<ActivityEmbeddingController> activityEmbeddingOptional,
Optional<MixedTransitionHandler> mixedTransitionHandler,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 483c8008ba65..ac510f89b905 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -50,6 +50,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
import com.android.wm.shell.apptoweb.AssistContentRequester;
+import com.android.wm.shell.appzoomout.AppZoomOutController;
import com.android.wm.shell.back.BackAnimationController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
@@ -1313,6 +1314,23 @@ public abstract class WMShellModule {
}
//
+ // App zoom out
+ //
+
+ @WMSingleton
+ @Provides
+ static AppZoomOutController provideAppZoomOutController(
+ Context context,
+ ShellInit shellInit,
+ ShellTaskOrganizer shellTaskOrganizer,
+ DisplayController displayController,
+ DisplayLayout displayLayout,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return AppZoomOutController.create(context, shellInit, shellTaskOrganizer,
+ displayController, displayLayout, mainExecutor);
+ }
+
+ //
// Drag and drop
//
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/appzoomout/AppZoomOutControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/appzoomout/AppZoomOutControllerTest.java
new file mode 100644
index 000000000000..e91a1238a390
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/appzoomout/AppZoomOutControllerTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 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.wm.shell.appzoomout;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.testing.AndroidTestingRunner;
+import android.view.Display;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.sysui.ShellInit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class AppZoomOutControllerTest extends ShellTestCase {
+
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+ @Mock private DisplayController mDisplayController;
+ @Mock private AppZoomOutDisplayAreaOrganizer mDisplayAreaOrganizer;
+ @Mock private ShellExecutor mExecutor;
+ @Mock private ActivityManager.RunningTaskInfo mRunningTaskInfo;
+
+ private AppZoomOutController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Display display = mContext.getDisplay();
+ DisplayLayout displayLayout = new DisplayLayout(mContext, display);
+ when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(displayLayout);
+
+ ShellInit shellInit = spy(new ShellInit(mExecutor));
+ mController = spy(new AppZoomOutController(mContext, shellInit, mTaskOrganizer,
+ mDisplayController, mDisplayAreaOrganizer, mExecutor));
+ }
+
+ @Test
+ public void isHomeTaskFocused_zoomOutForHome() {
+ mRunningTaskInfo.isFocused = true;
+ when(mRunningTaskInfo.getActivityType()).thenReturn(ACTIVITY_TYPE_HOME);
+ mController.onFocusTaskChanged(mRunningTaskInfo);
+
+ verify(mDisplayAreaOrganizer).setIsHomeTaskFocused(true);
+ }
+
+ @Test
+ public void isHomeTaskNotFocused_zoomOutForApp() {
+ mRunningTaskInfo.isFocused = false;
+ when(mRunningTaskInfo.getActivityType()).thenReturn(ACTIVITY_TYPE_HOME);
+ mController.onFocusTaskChanged(mRunningTaskInfo);
+
+ verify(mDisplayAreaOrganizer).setIsHomeTaskFocused(false);
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 2d7dc2e63650..0a0564994e69 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -43,6 +43,7 @@ import com.android.systemui.testKosmos
import com.android.systemui.util.WallpaperController
import com.android.systemui.util.mockito.eq
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
+import com.android.wm.shell.appzoomout.AppZoomOut
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
import org.junit.Before
@@ -65,6 +66,7 @@ import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
+import java.util.Optional
@RunWith(AndroidJUnit4::class)
@RunWithLooper
@@ -82,6 +84,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
@Mock private lateinit var wallpaperController: WallpaperController
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var appZoomOutOptional: Optional<AppZoomOut>
@Mock private lateinit var root: View
@Mock private lateinit var viewRootImpl: ViewRootImpl
@Mock private lateinit var windowToken: IBinder
@@ -128,6 +131,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
ResourcesSplitShadeStateController(),
windowRootViewBlurInteractor,
applicationScope,
+ appZoomOutOptional,
dumpManager,
configurationController,
)
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index f530522fb707..5f79c8cada45 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -100,7 +100,8 @@ public abstract class SystemUIInitializer {
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
.setRecentTasks(mWMComponent.getRecentTasks())
.setBackAnimation(mWMComponent.getBackAnimation())
- .setDesktopMode(mWMComponent.getDesktopMode());
+ .setDesktopMode(mWMComponent.getDesktopMode())
+ .setAppZoomOut(mWMComponent.getAppZoomOut());
// Only initialize when not starting from tests since this currently initializes some
// components that shouldn't be run in the test environment
@@ -121,7 +122,8 @@ public abstract class SystemUIInitializer {
.setStartingSurface(Optional.ofNullable(null))
.setRecentTasks(Optional.ofNullable(null))
.setBackAnimation(Optional.ofNullable(null))
- .setDesktopMode(Optional.ofNullable(null));
+ .setDesktopMode(Optional.ofNullable(null))
+ .setAppZoomOut(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 00eead6eb7fc..555fe6ef157d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -31,6 +31,7 @@ import com.android.systemui.statusbar.NotificationInsetsModule;
import com.android.systemui.statusbar.QsFrameTranslateModule;
import com.android.systemui.statusbar.phone.ConfigurationForwarder;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.appzoomout.AppZoomOut;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.desktopmode.DesktopMode;
@@ -115,6 +116,9 @@ public interface SysUIComponent {
@BindsInstance
Builder setDesktopMode(Optional<DesktopMode> d);
+ @BindsInstance
+ Builder setAppZoomOut(Optional<AppZoomOut> a);
+
SysUIComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 75117936c090..38f7c39203f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -34,6 +34,7 @@ import androidx.dynamicanimation.animation.SpringForce
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.TrackTracer
import com.android.systemui.Dumpable
+import com.android.systemui.Flags.spatialModelAppPushback
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -52,7 +53,9 @@ import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.WallpaperController
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
import com.android.systemui.window.flag.WindowBlurFlag
+import com.android.wm.shell.appzoomout.AppZoomOut
import java.io.PrintWriter
+import java.util.Optional
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.sign
@@ -79,6 +82,7 @@ constructor(
private val splitShadeStateController: SplitShadeStateController,
private val windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
@Application private val applicationScope: CoroutineScope,
+ private val appZoomOutOptional: Optional<AppZoomOut>,
dumpManager: DumpManager,
configurationController: ConfigurationController,
) : ShadeExpansionListener, Dumpable {
@@ -271,6 +275,13 @@ constructor(
private fun onBlurApplied(appliedBlurRadius: Int, zoomOutFromShadeRadius: Float) {
lastAppliedBlur = appliedBlurRadius
wallpaperController.setNotificationShadeZoom(zoomOutFromShadeRadius)
+ if (spatialModelAppPushback()) {
+ appZoomOutOptional.ifPresent { appZoomOut ->
+ appZoomOut.setProgress(
+ zoomOutFromShadeRadius
+ )
+ }
+ }
listeners.forEach {
it.onWallpaperZoomOutChanged(zoomOutFromShadeRadius)
it.onBlurRadiusChanged(appliedBlurRadius)
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index d49a507c9e11..5bec4424269a 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -25,6 +25,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.window.DisplayAreaOrganizer.FEATURE_APP_ZOOM_OUT;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
import static android.window.DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT;
@@ -151,6 +153,12 @@ public abstract class DisplayAreaPolicy {
.all()
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL,
TYPE_SECURE_SYSTEM_OVERLAY)
+ .build())
+ .addFeature(new Feature.Builder(wmService.mPolicy, "AppZoomOut",
+ FEATURE_APP_ZOOM_OUT)
+ .all()
+ .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL,
+ TYPE_STATUS_BAR, TYPE_NOTIFICATION_SHADE, TYPE_WALLPAPER)
.build());
}
rootHierarchy