summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java23
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java4
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java4
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java23
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java8
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java6
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java7
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java9
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java13
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java8
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java8
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TransactionManagerTest.java7
-rw-r--r--libs/WindowManager/Shell/Android.bp1
-rw-r--r--libs/WindowManager/Shell/OWNERS2
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml2
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl11
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java165
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt125
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java240
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt126
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt217
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java66
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java80
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHandler.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java108
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java93
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java136
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java21
-rw-r--r--libs/WindowManager/Shell/tests/OWNERS1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/OWNERS5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitLandscape.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitPortrait.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt118
-rw-r--r--libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragLandscape.kt44
-rw-r--r--libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragPortrait.kt43
-rw-r--r--libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/CloseAllAppsWithAppHeaderExit.kt77
-rw-r--r--libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithDrag.kt67
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt246
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java15
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt96
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt114
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt317
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java26
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java43
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java114
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java61
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt2
-rw-r--r--libs/hostgraphics/ADisplay.cpp12
-rw-r--r--libs/hostgraphics/Fence.cpp2
-rw-r--r--libs/hostgraphics/HostBufferQueue.cpp63
-rw-r--r--libs/hostgraphics/PublicFormat.cpp2
-rw-r--r--libs/hostgraphics/gui/BufferItem.h8
-rw-r--r--libs/hostgraphics/gui/BufferItemConsumer.h43
-rw-r--r--libs/hostgraphics/gui/BufferQueue.h2
-rw-r--r--libs/hostgraphics/gui/ConsumerBase.h4
-rw-r--r--libs/hostgraphics/gui/IGraphicBufferConsumer.h8
-rw-r--r--libs/hostgraphics/gui/IGraphicBufferProducer.h7
-rw-r--r--libs/hostgraphics/gui/Surface.h99
-rw-r--r--libs/hostgraphics/ui/Fence.h27
-rw-r--r--libs/hostgraphics/ui/GraphicBuffer.h58
-rw-r--r--libs/hwui/SkiaInterpolator.cpp1
-rw-r--r--libs/hwui/effects/GainmapRenderer.cpp22
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.cpp4
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp10
-rw-r--r--libs/hwui/renderthread/HintSessionWrapper.cpp5
-rw-r--r--libs/hwui/renderthread/HintSessionWrapper.h8
-rw-r--r--libs/hwui/renderthread/VulkanSurface.cpp15
-rw-r--r--libs/hwui/tests/unit/HintSessionWrapperTests.cpp50
-rw-r--r--libs/hwui/utils/Color.cpp2
129 files changed, 3092 insertions, 1082 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index 0a5a81b4fd8f..a0d6fce4c39b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -33,6 +33,7 @@ import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSI
import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT;
import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_TOP;
+import android.annotation.DimenRes;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.content.Context;
@@ -61,7 +62,6 @@ import android.window.TaskFragmentParentInfo;
import android.window.WindowContainerTransaction;
import androidx.annotation.GuardedBy;
-import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.window.extensions.core.util.function.Consumer;
import androidx.window.extensions.embedding.SplitAttributes.SplitType;
@@ -341,7 +341,7 @@ class DividerPresenter implements View.OnTouchListener {
applicationContext.getResources().getDisplayMetrics());
}
- private static int getDimensionDp(@IdRes int resId) {
+ private static int getDimensionDp(@DimenRes int resId) {
final Context context = ActivityThread.currentActivityThread().getApplication();
final int px = context.getResources().getDimensionPixelSize(resId);
return (int) TypedValue.convertPixelsToDimension(
@@ -431,6 +431,9 @@ class DividerPresenter implements View.OnTouchListener {
return null;
}
int widthDp = dividerAttributes.getWidthDp();
+ float minRatio = dividerAttributes.getPrimaryMinRatio();
+ float maxRatio = dividerAttributes.getPrimaryMaxRatio();
+
if (widthDp == WIDTH_SYSTEM_DEFAULT) {
widthDp = DEFAULT_DIVIDER_WIDTH_DP;
}
@@ -439,16 +442,14 @@ class DividerPresenter implements View.OnTouchListener {
// Draggable divider width must be larger than the drag handle size.
widthDp = Math.max(widthDp,
getDimensionDp(R.dimen.activity_embedding_divider_touch_target_width));
- }
-
- float minRatio = dividerAttributes.getPrimaryMinRatio();
- if (minRatio == RATIO_SYSTEM_DEFAULT) {
- minRatio = DEFAULT_MIN_RATIO;
- }
- float maxRatio = dividerAttributes.getPrimaryMaxRatio();
- if (maxRatio == RATIO_SYSTEM_DEFAULT) {
- maxRatio = DEFAULT_MAX_RATIO;
+ // Update minRatio and maxRatio only when it is a draggable divider.
+ if (minRatio == RATIO_SYSTEM_DEFAULT) {
+ minRatio = DEFAULT_MIN_RATIO;
+ }
+ if (maxRatio == RATIO_SYSTEM_DEFAULT) {
+ maxRatio = DEFAULT_MAX_RATIO;
+ }
}
return new DividerAttributes.Builder(dividerAttributes)
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 32f2d67888ae..a23a47416fb0 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -354,14 +354,14 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct,
@NonNull IBinder fragmentToken, boolean isolatedNav) {
final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
- OP_TYPE_SET_ISOLATED_NAVIGATION).setIsolatedNav(isolatedNav).build();
+ OP_TYPE_SET_ISOLATED_NAVIGATION).setBooleanValue(isolatedNav).build();
wct.addTaskFragmentOperation(fragmentToken, operation);
}
void setTaskFragmentDimOnTask(@NonNull WindowContainerTransaction wct,
@NonNull IBinder fragmentToken, boolean dimOnTask) {
final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
- OP_TYPE_SET_DIM_ON_TASK).setDimOnTask(dimOnTask).build();
+ OP_TYPE_SET_DIM_ON_TASK).setBooleanValue(dimOnTask).build();
wct.addTaskFragmentOperation(fragmentToken, operation);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 46a3e7f38bed..89d3058cc14d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -67,7 +67,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -113,8 +112,7 @@ import java.util.function.BiConsumer;
public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
ActivityEmbeddingComponent, DividerPresenter.DragEventCallback {
static final String TAG = "SplitController";
- static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
+ static final boolean ENABLE_SHELL_TRANSITIONS = true;
// TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without
// association. It's not set in WM Extensions nor Wm Jetpack library currently.
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
index de0171de4a37..8aca92e89e6b 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -59,7 +59,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.concurrent.Executor;
@@ -74,6 +75,9 @@ import java.util.concurrent.Executor;
@RunWith(AndroidJUnit4.class)
public class DividerPresenterTest {
@Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Rule
public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
private static final int MOCK_TASK_ID = 1234;
@@ -118,7 +122,6 @@ public class DividerPresenterTest {
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_INTERACTIVE_DIVIDER_FLAG);
when(mTaskContainer.getTaskId()).thenReturn(MOCK_TASK_ID);
@@ -263,6 +266,22 @@ public class DividerPresenterTest {
}
@Test
+ public void testSanitizeDividerAttributes_setDefaultValues_fixedDivider() {
+ DividerAttributes attributes =
+ new DividerAttributes.Builder(DividerAttributes.DIVIDER_TYPE_FIXED).build();
+ DividerAttributes sanitized = DividerPresenter.sanitizeDividerAttributes(attributes);
+
+ assertEquals(DividerAttributes.DIVIDER_TYPE_FIXED, sanitized.getDividerType());
+ assertEquals(DividerPresenter.DEFAULT_DIVIDER_WIDTH_DP, sanitized.getWidthDp());
+
+ // The ratios should not be set for fixed divider
+ assertEquals(DividerAttributes.RATIO_SYSTEM_DEFAULT, sanitized.getPrimaryMinRatio(),
+ 0.0f /* delta */);
+ assertEquals(DividerAttributes.RATIO_SYSTEM_DEFAULT, sanitized.getPrimaryMaxRatio(),
+ 0.0f /* delta */);
+ }
+
+ @Test
public void testSanitizeDividerAttributes_notChangingValidValues() {
DividerAttributes attributes =
new DividerAttributes.Builder(DividerAttributes.DIVIDER_TYPE_DRAGGABLE)
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 6f37e9cb794d..76e6a0ff2c21 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -42,10 +42,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
@@ -61,6 +63,9 @@ import java.util.ArrayList;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class JetpackTaskFragmentOrganizerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
@Mock
private WindowContainerTransaction mTransaction;
@Mock
@@ -73,7 +78,6 @@ public class JetpackTaskFragmentOrganizerTest {
@Before
public void setup() {
- MockitoAnnotations.initMocks(this);
mOrganizer = new JetpackTaskFragmentOrganizer(Runnable::run, mCallback);
mOrganizer.registerOrganizer();
spyOn(mOrganizer);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index b1b1984e3a70..50abdfd9a66c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -81,7 +81,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.List;
@@ -98,6 +99,8 @@ import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class OverlayPresentationTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
@Rule
public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
@@ -126,7 +129,6 @@ public class OverlayPresentationTest {
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
doReturn(new WindowLayoutInfo(new ArrayList<>())).when(mWindowLayoutComponent)
.getCurrentWindowLayoutInfo(anyInt(), any());
DeviceStateManagerFoldingFeatureProducer producer =
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 3441c2b26ea8..8bc3a300136a 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -111,7 +111,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.Collections;
@@ -137,6 +138,9 @@ public class SplitControllerTest {
new ComponentName("test", "placeholder"));
@Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Rule
public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
private Activity mActivity;
@@ -166,7 +170,6 @@ public class SplitControllerTest {
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
doReturn(new WindowLayoutInfo(new ArrayList<>())).when(mWindowLayoutComponent)
.getCurrentWindowLayoutInfo(anyInt(), any());
DeviceStateManagerFoldingFeatureProducer producer =
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 62d8aa30a576..c677484f64f1 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -85,10 +85,12 @@ import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutInfo;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
@@ -106,6 +108,10 @@ import java.util.ArrayList;
public class SplitPresenterTest {
private Activity mActivity;
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
@Mock
private Resources mActivityResources;
@Mock
@@ -119,7 +125,6 @@ public class SplitPresenterTest {
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
doReturn(new WindowLayoutInfo(new ArrayList<>())).when(mWindowLayoutComponent)
.getCurrentWindowLayoutInfo(anyInt(), any());
DeviceStateManagerFoldingFeatureProducer producer =
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index a5995a3027ac..8913b22115e9 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -42,11 +42,12 @@ import android.window.TaskFragmentParentInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.List;
@@ -60,14 +61,12 @@ import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TaskContainerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
@Mock
private SplitController mController;
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
@Test
public void testGetWindowingModeForSplitTaskFragment() {
final TaskContainer taskContainer = createTestTaskContainer();
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
index 379ea0c534ba..a1e9f08585f6 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
@@ -27,10 +27,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
/**
* Test class for {@link TaskFragmentAnimationController}.
@@ -42,13 +44,15 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TaskFragmentAnimationControllerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
@Mock
private TaskFragmentOrganizer mOrganizer;
private TaskFragmentAnimationController mAnimationController;
@Before
public void setup() {
- MockitoAnnotations.initMocks(this);
mAnimationController = new TaskFragmentAnimationController(mOrganizer);
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index 0af41791cf4a..abfc9c861d3e 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -51,10 +51,12 @@ import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import com.google.android.collect.Lists;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.List;
@@ -71,6 +73,9 @@ import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TaskFragmentContainerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
@Mock
private SplitPresenter mPresenter;
private SplitController mController;
@@ -83,7 +88,6 @@ public class TaskFragmentContainerTest {
@Before
public void setup() {
- MockitoAnnotations.initMocks(this);
DeviceStateManagerFoldingFeatureProducer producer =
mock(DeviceStateManagerFoldingFeatureProducer.class);
WindowLayoutComponentImpl component = mock(WindowLayoutComponentImpl.class);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TransactionManagerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TransactionManagerTest.java
index 459b6d2c31f9..2598dd63bbde 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TransactionManagerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TransactionManagerTest.java
@@ -41,10 +41,12 @@ import androidx.test.filters.SmallTest;
import androidx.window.extensions.embedding.TransactionManager.TransactionRecord;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
/**
* Test class for {@link TransactionManager}.
@@ -56,6 +58,8 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TransactionManagerTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
@Mock
private TaskFragmentOrganizer mOrganizer;
@@ -63,7 +67,6 @@ public class TransactionManagerTest {
@Before
public void setup() {
- MockitoAnnotations.initMocks(this);
mTransactionManager = new TransactionManager(mOrganizer);
}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 5c978e21b9bd..89781fd650a4 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -210,6 +210,7 @@ android_library {
"androidx.recyclerview_recyclerview",
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
+ "//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
"//frameworks/libs/systemui:iconloader_base",
"com_android_wm_shell_flags_lib",
"com.android.window.flags.window-aconfig-java",
diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
index 0c4fd140780e..ebebd8a52c9a 100644
--- a/libs/WindowManager/Shell/OWNERS
+++ b/libs/WindowManager/Shell/OWNERS
@@ -1,5 +1,5 @@
xutan@google.com
# Give submodule owners in shell resource approval
-per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com, nmusgrave@google.com, pbdr@google.com, tkachenkoi@google.com
+per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com, nmusgrave@google.com, pbdr@google.com, tkachenkoi@google.com, mpodolian@google.com, liranb@google.com
per-file res*/*/tv_*.xml = bronger@google.com
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 7b5c471e074f..bcdc2a9c8539 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -62,7 +62,7 @@
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> von <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aus <xliff:g id="APP_NAME">%2$s</xliff:g> und <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> weiteren"</string>
<string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Nach oben links verschieben"</string>
- <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Nach rechts oben verschieben"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Nach oben rechts verschieben"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Nach unten links verschieben"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Nach unten rechts verschieben"</string>
<string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> maximieren"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 89db32793b32..c371f7f62feb 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -61,10 +61,10 @@
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Engadir de novo á pilla"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> e <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> máis"</string>
- <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover á parte super. esquerda"</string>
- <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover á parte superior dereita"</string>
- <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover á parte infer. esquerda"</string>
- <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover á parte inferior dereita"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover arriba á esquerda"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover arriba á dereita"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover abaixo á esquerda"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover abaixo á dereita"</string>
<string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"despregar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index e4830af1bad6..a5bd2ab5c10b 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -69,7 +69,7 @@
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> कोल्याप्स गर्नुहोस्"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> का सेटिङहरू"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारेज गर्नुहोस्"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"वार्तालाप बबलको रूपमा नदेखाइयोस्"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"वार्तालाप बबलको रूपमा नदेखाउनुहोस्"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"बबलहरू प्रयोग गरी कुराकानी गर्नुहोस्"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"नयाँ वार्तालापहरू तैरने आइकन वा बबलका रूपमा देखिन्छन्। बबल खोल्न ट्याप गर्नुहोस्। बबल सार्न सो बबललाई ड्र्याग गर्नुहोस्।"</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"जुनसुकै बेला बबलहरू नियन्त्रण गर्नुहोस्"</string>
@@ -99,7 +99,7 @@
<string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"यो एप तपाईंको स्क्रिनमा अझ राम्रोसँग देखियोस् भन्नाका लागि तपाईं सो एप रिस्टार्ट गर्न सक्नुहुन्छ तर तपाईंले अहिलेसम्म गरेका क्रियाकलाप वा सेभ गर्न बाँकी परिवर्तनहरू हट्न सक्छन्"</string>
<string name="letterbox_restart_cancel" msgid="1342209132692537805">"रद्द गर्नुहोस्"</string>
<string name="letterbox_restart_restart" msgid="8529976234412442973">"रिस्टार्ट गर्नुहोस्"</string>
- <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"फेरि नदेखाइयोस्"</string>
+ <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"फेरि नदेखाउनुहोस्"</string>
<string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"यो एप सार्न डबल\nट्याप गर्नुहोस्"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ठुलो बनाउनुहोस्"</string>
<string name="minimize_button_text" msgid="271592547935841753">"मिनिमाइज गर्नुहोस्"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index ff77d3b2d150..1210fe8fda05 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -62,9 +62,9 @@
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> do <xliff:g id="APP_NAME">%2$s</xliff:g> e mais<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>."</string>
<string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover p/ parte sup. esquerda"</string>
- <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover parte superior direita"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover p/ parte sup. direita"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover p/ parte infer. esquerda"</string>
- <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover parte inferior direita"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover p/ parte inf. direita"</string>
<string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expandir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"reduzir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Definições de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index e23c1ff19563..971e146ba77e 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -61,10 +61,10 @@
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Добавить обратно в стек"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> из приложения \"<xliff:g id="APP_NAME">%2$s</xliff:g>\""</string>
<string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> от приложения \"<xliff:g id="APP_NAME">%2$s</xliff:g>\" и ещё <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
- <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Перенести в левый верхний угол"</string>
- <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перенести в правый верхний угол"</string>
- <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перенести в левый нижний угол"</string>
- <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перенести в правый нижний угол"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Переместить в левый верхний угол"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Переместить в правый верхний угол"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Переместить в левый нижний угол"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Переместить в правый нижний угол"</string>
<string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Развернуть <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Свернуть <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: настройки"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 407fbbbb1707..fe0b74c469f4 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -64,7 +64,7 @@
<string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ย้ายไปด้านซ้ายบน"</string>
<string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ย้ายไปด้านขวาบน"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ย้ายไปด้านซ้ายล่าง"</string>
- <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ย้ายไปด้านขาวล่าง"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ย้ายไปด้านขวาล่าง"</string>
<string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ขยาย <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ยุบ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"การตั้งค่า <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl
index 526407e25d98..3256abf09116 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl
@@ -28,13 +28,14 @@ import com.android.wm.shell.shared.IHomeTransitionListener;
interface IShellTransitions {
/**
- * Registers a remote transition handler.
+ * Registers a remote transition handler for all operations excluding takeovers (see
+ * registerRemoteForTakeover()).
*/
oneway void registerRemote(in TransitionFilter filter,
in RemoteTransition remoteTransition) = 1;
/**
- * Unregisters a remote transition handler.
+ * Unregisters a remote transition handler for all operations.
*/
oneway void unregisterRemote(in RemoteTransition remoteTransition) = 2;
@@ -52,4 +53,10 @@ interface IShellTransitions {
* Returns a container surface for the home root task.
*/
SurfaceControl getHomeTaskOverlayContainer() = 5;
+
+ /**
+ * Registers a remote transition for takeover operations only.
+ */
+ oneway void registerRemoteForTakeover(in TransitionFilter filter,
+ in RemoteTransition remoteTransition) = 6;
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java
index 5e49f559ca64..6d4ab4c1bd09 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java
@@ -28,13 +28,20 @@ import com.android.wm.shell.shared.annotations.ExternalThread;
@ExternalThread
public interface ShellTransitions {
/**
- * Registers a remote transition.
+ * Registers a remote transition for all operations excluding takeovers (see
+ * {@link ShellTransitions#registerRemoteForTakeover(TransitionFilter, RemoteTransition)}).
*/
default void registerRemote(@NonNull TransitionFilter filter,
@NonNull RemoteTransition remoteTransition) {}
/**
- * Unregisters a remote transition.
+ * Registers a remote transition for takeover operations only.
+ */
+ default void registerRemoteForTakeover(@NonNull TransitionFilter filter,
+ @NonNull RemoteTransition remoteTransition) {}
+
+ /**
+ * Unregisters a remote transition for all operations.
*/
default void unregisterRemote(@NonNull RemoteTransition remoteTransition) {}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index d8d0d876b4f2..3244837324b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -16,6 +16,7 @@
package com.android.wm.shell;
+
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -30,7 +31,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.AppCompatTaskInfo;
+import android.app.CameraCompatTaskInfo.CameraCompatControlState;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.content.LocusId;
@@ -718,8 +719,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
@Override
- public void onCameraControlStateUpdated(
- int taskId, @AppCompatTaskInfo.CameraCompatControlState int state) {
+ public void onCameraControlStateUpdated(int taskId, @CameraCompatControlState int state) {
final TaskAppearedInfo info;
synchronized (mLock) {
info = mTasks.get(taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
index 26432111efdc..196f89d5794e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -49,9 +49,9 @@ public interface BackAnimation {
@BackEvent.SwipeEdge int swipeEdge);
/**
- * Called when the input pointers are pilfered.
+ * Called when the back swipe threshold is crossed.
*/
- void onPilferPointers();
+ void onThresholdCrossed();
/**
* Sets whether the back gesture is past the trigger threshold or not.
@@ -101,4 +101,10 @@ public interface BackAnimation {
* @param customizer the controller to control system bar color.
*/
void setStatusBarCustomizer(StatusBarCustomizer customizer);
+
+ /**
+ * Set a callback to pilfer pointers.
+ * @param pilferCallback the callback to pilfer pointers.
+ */
+ void setPilferPointerCallback(Runnable pilferCallback);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index d3fe4f82daf7..163a896e2659 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -22,9 +22,6 @@ import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTas
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -46,7 +43,6 @@ import android.os.UserHandle;
import android.provider.Settings.Global;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.MathUtils;
import android.view.IRemoteAnimationRunner;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
@@ -57,6 +53,7 @@ import android.window.BackAnimationAdapter;
import android.window.BackEvent;
import android.window.BackMotionEvent;
import android.window.BackNavigationInfo;
+import android.window.BackTouchTracker;
import android.window.IBackAnimationFinishedCallback;
import android.window.IBackAnimationRunner;
import android.window.IOnBackInvokedCallback;
@@ -118,7 +115,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
/** Tracks if we should start the back gesture on the next motion move event */
private boolean mShouldStartOnNextMoveEvent = false;
private boolean mOnBackStartDispatched = false;
- private boolean mPointerPilfered = false;
+ private boolean mThresholdCrossed = false;
+ private boolean mPointersPilfered = false;
private final boolean mRequirePointerPilfer;
private final FlingAnimationUtils mFlingAnimationUtils;
@@ -139,13 +137,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
/**
* Tracks the current user back gesture.
*/
- private TouchTracker mCurrentTracker = new TouchTracker();
+ private BackTouchTracker mCurrentTracker = new BackTouchTracker();
/**
* Tracks the next back gesture in case a new user gesture has started while the back animation
* (and navigation) associated with {@link #mCurrentTracker} have not yet finished.
*/
- private TouchTracker mQueuedTracker = new TouchTracker();
+ private BackTouchTracker mQueuedTracker = new BackTouchTracker();
private final Runnable mAnimationTimeoutRunnable = () -> {
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation didn't finish in %d ms. Resetting...",
@@ -189,6 +187,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
// Keep previous navigation type before remove mBackNavigationInfo.
@BackNavigationInfo.BackTargetType
private int mPreviousNavigationType;
+ private Runnable mPilferPointerCallback;
public BackAnimationController(
@NonNull ShellInit shellInit,
@@ -335,8 +334,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
@Override
- public void onPilferPointers() {
- BackAnimationController.this.onPilferPointers();
+ public void onThresholdCrossed() {
+ BackAnimationController.this.onThresholdCrossed();
}
@Override
@@ -358,6 +357,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mCustomizer = customizer;
mAnimationBackground.setStatusBarCustomizer(customizer);
}
+
+ @Override
+ public void setPilferPointerCallback(Runnable callback) {
+ mShellExecutor.execute(() -> {
+ mPilferPointerCallback = callback;
+ });
+ }
}
private static class IBackAnimationImpl extends IBackAnimation.Stub
@@ -414,20 +420,23 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mShellBackAnimationRegistry.unregisterAnimation(type);
}
- private TouchTracker getActiveTracker() {
+ private BackTouchTracker getActiveTracker() {
if (mCurrentTracker.isActive()) return mCurrentTracker;
if (mQueuedTracker.isActive()) return mQueuedTracker;
return null;
}
@VisibleForTesting
- void onPilferPointers() {
- mPointerPilfered = true;
+ public void onThresholdCrossed() {
+ mThresholdCrossed = true;
// Dispatch onBackStarted, only to app callbacks.
// System callbacks will receive onBackStarted when the remote animation starts.
- if (!shouldDispatchToAnimator() && mActiveCallback != null) {
+ final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
+ if (!shouldDispatchToAnimator && mActiveCallback != null) {
mCurrentTracker.updateStartLocation();
tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+ } else if (shouldDispatchToAnimator) {
+ tryPilferPointers();
}
}
@@ -443,7 +452,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
int keyAction,
@BackEvent.SwipeEdge int swipeEdge) {
- TouchTracker activeTouchTracker = getActiveTracker();
+ BackTouchTracker activeTouchTracker = getActiveTracker();
if (activeTouchTracker != null) {
activeTouchTracker.update(touchX, touchY, velocityX, velocityY);
}
@@ -487,7 +496,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
// onBackCancelled event, let's interrupt it and start animating a new back gesture
resetTouchTracker();
}
- TouchTracker touchTracker;
+ BackTouchTracker touchTracker;
if (mCurrentTracker.isInInitialState()) {
touchTracker = mCurrentTracker;
} else if (mQueuedTracker.isInInitialState()) {
@@ -498,7 +507,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return;
}
touchTracker.setGestureStartLocation(touchX, touchY, swipeEdge);
- touchTracker.setState(TouchTracker.TouchTrackerState.ACTIVE);
+ touchTracker.setState(BackTouchTracker.TouchTrackerState.ACTIVE);
mBackGestureStarted = true;
if (interruptCancelPostCommitAnimation) {
@@ -514,7 +523,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
- private void startBackNavigation(@NonNull TouchTracker touchTracker) {
+ private void startBackNavigation(@NonNull BackTouchTracker touchTracker) {
try {
startLatencyTracking();
mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
@@ -527,7 +536,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
private void onBackNavigationInfoReceived(@Nullable BackNavigationInfo backNavigationInfo,
- @NonNull TouchTracker touchTracker) {
+ @NonNull BackTouchTracker touchTracker) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Received backNavigationInfo:%s", backNavigationInfo);
if (backNavigationInfo == null) {
ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Received BackNavigationInfo is null.");
@@ -540,6 +549,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (!mShellBackAnimationRegistry.startGesture(backType)) {
mActiveCallback = null;
}
+ tryPilferPointers();
} else {
mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
// App is handling back animation. Cancel system animation latency tracking.
@@ -588,12 +598,22 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
&& mBackNavigationInfo.isPrepareRemoteAnimation();
}
+ private void tryPilferPointers() {
+ if (mPointersPilfered || !mThresholdCrossed) {
+ return;
+ }
+ if (mPilferPointerCallback != null) {
+ mPilferPointerCallback.run();
+ }
+ mPointersPilfered = true;
+ }
+
private void tryDispatchOnBackStarted(
IOnBackInvokedCallback callback,
BackMotionEvent backEvent) {
if (mOnBackStartDispatched
|| callback == null
- || (!mPointerPilfered && mRequirePointerPilfer)) {
+ || (!mThresholdCrossed && mRequirePointerPilfer)) {
return;
}
dispatchOnBackStarted(callback, backEvent);
@@ -613,79 +633,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
-
- /**
- * Allows us to manage the fling gesture, it smoothly animates the current progress value to
- * the final position, calculated based on the current velocity.
- *
- * @param callback the callback to be invoked when the animation ends.
- */
- private void dispatchOrAnimateOnBackInvoked(IOnBackInvokedCallback callback,
- @NonNull TouchTracker touchTracker) {
- if (callback == null) {
- return;
- }
-
- boolean animationStarted = false;
-
- if (mBackNavigationInfo != null && mBackNavigationInfo.isAnimationCallback()) {
-
- final BackMotionEvent backMotionEvent = touchTracker.createProgressEvent();
- if (backMotionEvent != null) {
- // Constraints - absolute values
- float minVelocity = mFlingAnimationUtils.getMinVelocityPxPerSecond();
- float maxVelocity = mFlingAnimationUtils.getHighVelocityPxPerSecond();
- float maxX = touchTracker.getMaxDistance(); // px
- float maxFlingDistance = maxX * MAX_FLING_PROGRESS; // px
-
- // Current state
- float currentX = backMotionEvent.getTouchX();
- float velocity = MathUtils.constrain(backMotionEvent.getVelocityX(),
- -maxVelocity, maxVelocity);
-
- // Target state
- float animationFaction = velocity / maxVelocity; // value between -1 and 1
- float flingDistance = animationFaction * maxFlingDistance; // px
- float endX = MathUtils.constrain(currentX + flingDistance, 0f, maxX);
-
- if (!Float.isNaN(endX)
- && currentX != endX
- && Math.abs(velocity) >= minVelocity) {
- ValueAnimator animator = ValueAnimator.ofFloat(currentX, endX);
-
- mFlingAnimationUtils.apply(
- /* animator = */ animator,
- /* currValue = */ currentX,
- /* endValue = */ endX,
- /* velocity = */ velocity,
- /* maxDistance = */ maxFlingDistance
- );
-
- animator.addUpdateListener(animation -> {
- Float animatedValue = (Float) animation.getAnimatedValue();
- float progress = touchTracker.getProgress(animatedValue);
- final BackMotionEvent backEvent = touchTracker.createProgressEvent(
- progress);
- dispatchOnBackProgressed(mActiveCallback, backEvent);
- });
-
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- dispatchOnBackInvoked(callback);
- }
- });
- animator.start();
- animationStarted = true;
- }
- }
- }
-
- if (!animationStarted) {
- dispatchOnBackInvoked(callback);
- }
- }
-
private void dispatchOnBackInvoked(IOnBackInvokedCallback callback) {
if (callback == null) {
return;
@@ -714,7 +661,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private void dispatchOnBackProgressed(IOnBackInvokedCallback callback,
BackMotionEvent backEvent) {
- if (callback == null) {
+ if (callback == null || !shouldDispatchToAnimator()) {
return;
}
try {
@@ -728,7 +675,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* Sets to true when the back gesture has passed the triggering threshold, false otherwise.
*/
public void setTriggerBack(boolean triggerBack) {
- TouchTracker activeBackGestureInfo = getActiveTracker();
+ if (mActiveCallback != null) {
+ try {
+ mActiveCallback.setTriggerBack(triggerBack);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote setTriggerBack error: ", e);
+ }
+ }
+ BackTouchTracker activeBackGestureInfo = getActiveTracker();
if (activeBackGestureInfo != null) {
activeBackGestureInfo.setTriggerBack(triggerBack);
}
@@ -742,7 +696,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mQueuedTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
}
- private void invokeOrCancelBack(@NonNull TouchTracker touchTracker) {
+ private void invokeOrCancelBack(@NonNull BackTouchTracker touchTracker) {
// Make a synchronized call to core before dispatch back event to client side.
// If the close transition happens before the core receives onAnimationFinished, there will
// play a second close animation for that transition.
@@ -758,7 +712,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (mBackNavigationInfo != null) {
final IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
if (touchTracker.getTriggerBack()) {
- dispatchOrAnimateOnBackInvoked(callback, touchTracker);
+ dispatchOnBackInvoked(callback);
} else {
tryDispatchOnBackCancelled(callback);
}
@@ -770,7 +724,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* Called when the gesture is released, then it could start the post commit animation.
*/
private void onGestureFinished() {
- TouchTracker activeTouchTracker = getActiveTracker();
+ BackTouchTracker activeTouchTracker = getActiveTracker();
if (!mBackGestureStarted || activeTouchTracker == null) {
// This can happen when an unfinished gesture has been reset in resetTouchTracker
ProtoLog.d(WM_SHELL_BACK_PREVIEW,
@@ -780,8 +734,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
boolean triggerBack = activeTouchTracker.getTriggerBack();
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", triggerBack);
+ // Reset gesture states.
+ mThresholdCrossed = false;
+ mPointersPilfered = false;
mBackGestureStarted = false;
- activeTouchTracker.setState(TouchTracker.TouchTrackerState.FINISHED);
+ activeTouchTracker.setState(BackTouchTracker.TouchTrackerState.FINISHED);
if (mPostCommitAnimationInProgress) {
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation is still running");
@@ -839,7 +796,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (mCurrentTracker.getTriggerBack()) {
// notify gesture finished
mBackNavigationInfo.onBackGestureFinished(true);
- dispatchOrAnimateOnBackInvoked(mActiveCallback, mCurrentTracker);
+ dispatchOnBackInvoked(mActiveCallback);
} else {
tryDispatchOnBackCancelled(mActiveCallback);
}
@@ -882,10 +839,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
/**
- * Resets the TouchTracker and potentially starts a new back navigation in case one is queued
+ * Resets the BackTouchTracker and potentially starts a new back navigation in case one
+ * is queued.
*/
private void resetTouchTracker() {
- TouchTracker temp = mCurrentTracker;
+ BackTouchTracker temp = mCurrentTracker;
mCurrentTracker = mQueuedTracker;
temp.reset();
mQueuedTracker = temp;
@@ -928,7 +886,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mApps = null;
mShouldStartOnNextMoveEvent = false;
mOnBackStartDispatched = false;
- mPointerPilfered = false;
+ mThresholdCrossed = false;
+ mPointersPilfered = false;
mShellBackAnimationRegistry.resetDefaultCrossActivity();
cancelLatencyTracking();
if (mBackNavigationInfo != null) {
@@ -1076,7 +1035,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
pw.println(prefix + " mBackGestureStarted=" + mBackGestureStarted);
pw.println(prefix + " mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress);
pw.println(prefix + " mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent);
- pw.println(prefix + " mPointerPilfered=" + mPointerPilfered);
+ pw.println(prefix + " mPointerPilfered=" + mThresholdCrossed);
pw.println(prefix + " mRequirePointerPilfer=" + mRequirePointerPilfer);
pw.println(prefix + " mCurrentTracker state:");
mCurrentTracker.dump(pw, prefix + " ");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index 7cb56605cc12..037b1ec9247c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -20,6 +20,7 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.Configuration
+import android.graphics.Color
import android.graphics.Matrix
import android.graphics.PointF
import android.graphics.Rect
@@ -35,6 +36,7 @@ import android.view.animation.DecelerateInterpolator
import android.view.animation.Interpolator
import android.window.BackEvent
import android.window.BackMotionEvent
+import android.window.BackNavigationInfo
import android.window.BackProgressAnimator
import android.window.IOnBackInvokedCallback
import com.android.internal.jank.Cuj
@@ -67,6 +69,7 @@ class CrossActivityBackAnimation @Inject constructor(
private val currentEnteringRect = RectF()
private val backAnimRect = Rect()
+ private val cropRect = Rect()
private var cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
@@ -94,6 +97,12 @@ class CrossActivityBackAnimation @Inject constructor(
private var scrimLayer: SurfaceControl? = null
private var maxScrimAlpha: Float = 0f
+ private var isLetterboxed = false
+ private var enteringHasSameLetterbox = false
+ private var leftLetterboxLayer: SurfaceControl? = null
+ private var rightLetterboxLayer: SurfaceControl? = null
+ private var letterboxColor: Int = 0
+
override fun onConfigurationChanged(newConfiguration: Configuration) {
cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
}
@@ -112,9 +121,19 @@ class CrossActivityBackAnimation @Inject constructor(
initialTouchPos.set(backMotionEvent.touchX, backMotionEvent.touchY)
transaction.setAnimationTransaction()
-
+ isLetterboxed = closingTarget!!.taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed
+ enteringHasSameLetterbox = isLetterboxed &&
+ closingTarget!!.localBounds.equals(enteringTarget!!.localBounds)
+
+ if (isLetterboxed && !enteringHasSameLetterbox) {
+ // Play animation with letterboxes, if closing and entering target have mismatching
+ // letterboxes
+ backAnimRect.set(closingTarget!!.windowConfiguration.bounds)
+ } else {
+ // otherwise play animation on localBounds only
+ backAnimRect.set(closingTarget!!.localBounds)
+ }
// Offset start rectangle to align task bounds.
- backAnimRect.set(closingTarget!!.localBounds)
backAnimRect.offsetTo(0, 0)
startClosingRect.set(backAnimRect)
@@ -136,12 +155,25 @@ class CrossActivityBackAnimation @Inject constructor(
targetEnteringRect.set(startEnteringRect)
targetEnteringRect.scaleCentered(MAX_SCALE)
- // Draw background with task background color.
+ // Draw background with task background color (or letterbox color).
+ val backgroundColor = if (isLetterboxed) {
+ letterboxColor
+ } else {
+ enteringTarget!!.taskInfo.taskDescription!!.backgroundColor
+ }
background.ensureBackground(
- closingTarget!!.windowConfiguration.bounds,
- enteringTarget!!.taskInfo.taskDescription!!.backgroundColor, transaction
+ closingTarget!!.windowConfiguration.bounds, backgroundColor, transaction
)
ensureScrimLayer()
+ if (isLetterboxed && enteringHasSameLetterbox) {
+ // crop left and right letterboxes
+ cropRect.set(closingTarget!!.localBounds.left, 0, closingTarget!!.localBounds.right,
+ closingTarget!!.windowConfiguration.bounds.height())
+ // and add fake letterbox square surfaces instead
+ ensureLetterboxes()
+ } else {
+ cropRect.set(backAnimRect)
+ }
applyTransaction()
}
@@ -241,17 +273,25 @@ class CrossActivityBackAnimation @Inject constructor(
}
finishCallback = null
removeScrimLayer()
+ removeLetterbox()
+ isLetterboxed = false
+ enteringHasSameLetterbox = false
}
private fun applyTransform(leash: SurfaceControl?, rect: RectF, alpha: Float) {
if (leash == null || !leash.isValid) return
val scale = rect.width() / backAnimRect.width()
transformMatrix.reset()
- transformMatrix.setScale(scale, scale)
+ val scalePivotX = if (isLetterboxed && enteringHasSameLetterbox) {
+ closingTarget!!.localBounds.left.toFloat()
+ } else {
+ 0f
+ }
+ transformMatrix.setScale(scale, scale, scalePivotX, 0f)
transformMatrix.postTranslate(rect.left, rect.top)
transaction.setAlpha(leash, alpha)
.setMatrix(leash, transformMatrix, tmpFloat9)
- .setCrop(leash, backAnimRect)
+ .setCrop(leash, cropRect)
.setCornerRadius(leash, cornerRadius)
}
@@ -274,24 +314,87 @@ class CrossActivityBackAnimation @Inject constructor(
scrimLayer = scrimBuilder.build()
val colorComponents = floatArrayOf(0f, 0f, 0f)
maxScrimAlpha = if (isDarkTheme) MAX_SCRIM_ALPHA_DARK else MAX_SCRIM_ALPHA_LIGHT
+ val scrimCrop = if (isLetterboxed) {
+ closingTarget!!.windowConfiguration.bounds
+ } else {
+ closingTarget!!.localBounds
+ }
transaction
.setColor(scrimLayer, colorComponents)
.setAlpha(scrimLayer!!, maxScrimAlpha)
- .setCrop(scrimLayer!!, closingTarget!!.localBounds)
+ .setCrop(scrimLayer!!, scrimCrop)
.setRelativeLayer(scrimLayer!!, closingTarget!!.leash, -1)
.show(scrimLayer)
}
private fun removeScrimLayer() {
- scrimLayer?.let {
+ if (removeLayer(scrimLayer)) applyTransaction()
+ scrimLayer = null
+ }
+
+ /**
+ * Adds two "fake" letterbox square surfaces to the left and right of the localBounds of the
+ * closing target
+ */
+ private fun ensureLetterboxes() {
+ closingTarget?.let { t ->
+ if (t.localBounds.left != 0 && leftLetterboxLayer == null) {
+ val bounds = Rect(0, t.windowConfiguration.bounds.top, t.localBounds.left,
+ t.windowConfiguration.bounds.bottom)
+ leftLetterboxLayer = ensureLetterbox(bounds)
+ }
+ if (t.localBounds.right != t.windowConfiguration.bounds.right &&
+ rightLetterboxLayer == null) {
+ val bounds = Rect(t.localBounds.right, t.windowConfiguration.bounds.top,
+ t.windowConfiguration.bounds.right, t.windowConfiguration.bounds.bottom)
+ rightLetterboxLayer = ensureLetterbox(bounds)
+ }
+ }
+ }
+
+ private fun ensureLetterbox(bounds: Rect): SurfaceControl {
+ val letterboxBuilder = SurfaceControl.Builder()
+ .setName("Cross-Activity back animation letterbox")
+ .setCallsite("CrossActivityBackAnimation")
+ .setColorLayer()
+ .setOpaque(true)
+ .setHidden(false)
+
+ rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, letterboxBuilder)
+ val layer = letterboxBuilder.build()
+ val colorComponents = floatArrayOf(Color.red(letterboxColor) / 255f,
+ Color.green(letterboxColor) / 255f, Color.blue(letterboxColor) / 255f)
+ transaction
+ .setColor(layer, colorComponents)
+ .setCrop(layer, bounds)
+ .setRelativeLayer(layer, closingTarget!!.leash, 1)
+ .show(layer)
+ return layer
+ }
+
+ private fun removeLetterbox() {
+ if (removeLayer(leftLetterboxLayer) || removeLayer(rightLetterboxLayer)) applyTransaction()
+ leftLetterboxLayer = null
+ rightLetterboxLayer = null
+ }
+
+ private fun removeLayer(layer: SurfaceControl?): Boolean {
+ layer?.let {
if (it.isValid) {
transaction.remove(it)
- applyTransaction()
+ return true
}
}
- scrimLayer = null
+ return false
}
+ override fun prepareNextAnimation(
+ animationInfo: BackNavigationInfo.CustomAnimationInfo?,
+ letterboxColor: Int
+ ): Boolean {
+ this.letterboxColor = letterboxColor
+ return false
+ }
private inner class Callback : IOnBackInvokedCallback.Default() {
override fun onBackStarted(backMotionEvent: BackMotionEvent) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
index 838dab43d6e8..e27b40e58591 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
@@ -271,7 +271,8 @@ public class CustomizeActivityAnimation extends ShellBackAnimation {
/** Load customize animation before animation start. */
@Override
- public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) {
+ public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo,
+ int letterboxColor) {
if (animationInfo == null) {
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java
index 8a0daaa72e24..9cd193b0f74c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java
@@ -42,11 +42,12 @@ public abstract class ShellBackAnimation {
public abstract BackAnimationRunner getRunner();
/**
- * Prepare the next animation with customized animation.
+ * Prepare the next animation.
*
* @return true if this type of back animation should override the default.
*/
- public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) {
+ public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo,
+ int letterboxColor) {
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
index 7a6032c60cce..6fafa75e2f70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
@@ -154,11 +154,14 @@ public class ShellBackAnimationRegistry {
if (type == BackNavigationInfo.TYPE_CROSS_ACTIVITY && mAnimationDefinition.contains(type)) {
if (mCustomizeActivityAnimation != null
&& mCustomizeActivityAnimation.prepareNextAnimation(
- backNavigationInfo.getCustomAnimationInfo())) {
+ backNavigationInfo.getCustomAnimationInfo(), 0)) {
mAnimationDefinition.get(type).resetWaitingAnimation();
mAnimationDefinition.set(
BackNavigationInfo.TYPE_CROSS_ACTIVITY,
mCustomizeActivityAnimation.getRunner());
+ } else if (mDefaultCrossActivityAnimation != null) {
+ mDefaultCrossActivityAnimation.prepareNextAnimation(null,
+ backNavigationInfo.getLetterboxColor());
}
}
BackAnimationRunner runner = mAnimationDefinition.get(type);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
deleted file mode 100644
index 8f04f126960c..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2022 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.back;
-
-import android.annotation.FloatRange;
-import android.os.SystemProperties;
-import android.util.MathUtils;
-import android.view.MotionEvent;
-import android.view.RemoteAnimationTarget;
-import android.window.BackEvent;
-import android.window.BackMotionEvent;
-
-import java.io.PrintWriter;
-
-/**
- * Helper class to record the touch location for gesture and generate back events.
- */
-class TouchTracker {
- private static final String PREDICTIVE_BACK_LINEAR_DISTANCE_PROP =
- "persist.wm.debug.predictive_back_linear_distance";
- private static final int LINEAR_DISTANCE = SystemProperties
- .getInt(PREDICTIVE_BACK_LINEAR_DISTANCE_PROP, -1);
- private float mLinearDistance = LINEAR_DISTANCE;
- private float mMaxDistance;
- private float mNonLinearFactor;
- /**
- * Location of the latest touch event
- */
- private float mLatestTouchX;
- private float mLatestTouchY;
- private boolean mTriggerBack;
-
- /**
- * Location of the initial touch event of the back gesture.
- */
- private float mInitTouchX;
- private float mInitTouchY;
- private float mLatestVelocityX;
- private float mLatestVelocityY;
- private float mStartThresholdX;
- private int mSwipeEdge;
- private TouchTrackerState mState = TouchTrackerState.INITIAL;
-
- void update(float touchX, float touchY, float velocityX, float velocityY) {
- /**
- * If back was previously cancelled but the user has started swiping in the forward
- * direction again, restart back.
- */
- if ((touchX < mStartThresholdX && mSwipeEdge == BackEvent.EDGE_LEFT)
- || (touchX > mStartThresholdX && mSwipeEdge == BackEvent.EDGE_RIGHT)) {
- mStartThresholdX = touchX;
- if ((mSwipeEdge == BackEvent.EDGE_LEFT && mStartThresholdX < mInitTouchX)
- || (mSwipeEdge == BackEvent.EDGE_RIGHT && mStartThresholdX > mInitTouchX)) {
- mInitTouchX = mStartThresholdX;
- }
- }
- mLatestTouchX = touchX;
- mLatestTouchY = touchY;
- mLatestVelocityX = velocityX;
- mLatestVelocityY = velocityY;
- }
-
- void setTriggerBack(boolean triggerBack) {
- if (mTriggerBack != triggerBack && !triggerBack) {
- mStartThresholdX = mLatestTouchX;
- }
- mTriggerBack = triggerBack;
- }
-
- boolean getTriggerBack() {
- return mTriggerBack;
- }
-
- void setState(TouchTrackerState state) {
- mState = state;
- }
-
- boolean isInInitialState() {
- return mState == TouchTrackerState.INITIAL;
- }
-
- boolean isActive() {
- return mState == TouchTrackerState.ACTIVE;
- }
-
- boolean isFinished() {
- return mState == TouchTrackerState.FINISHED;
- }
-
- void setGestureStartLocation(float touchX, float touchY, int swipeEdge) {
- mInitTouchX = touchX;
- mInitTouchY = touchY;
- mLatestTouchX = touchX;
- mLatestTouchY = touchY;
- mSwipeEdge = swipeEdge;
- mStartThresholdX = mInitTouchX;
- }
-
- /** Update the start location used to compute the progress
- * to the latest touch location.
- */
- void updateStartLocation() {
- mInitTouchX = mLatestTouchX;
- mInitTouchY = mLatestTouchY;
- mStartThresholdX = mInitTouchX;
- }
-
- void reset() {
- mInitTouchX = 0;
- mInitTouchY = 0;
- mStartThresholdX = 0;
- mTriggerBack = false;
- mState = TouchTrackerState.INITIAL;
- mSwipeEdge = BackEvent.EDGE_LEFT;
- }
-
- BackMotionEvent createStartEvent(RemoteAnimationTarget target) {
- return new BackMotionEvent(
- /* touchX = */ mInitTouchX,
- /* touchY = */ mInitTouchY,
- /* progress = */ 0,
- /* velocityX = */ 0,
- /* velocityY = */ 0,
- /* triggerBack = */ mTriggerBack,
- /* swipeEdge = */ mSwipeEdge,
- /* departingAnimationTarget = */ target);
- }
-
- BackMotionEvent createProgressEvent() {
- float progress = getProgress(mLatestTouchX);
- return createProgressEvent(progress);
- }
-
- /**
- * Progress value computed from the touch position.
- *
- * @param touchX the X touch position of the {@link MotionEvent}.
- * @return progress value
- */
- @FloatRange(from = 0.0, to = 1.0)
- float getProgress(float touchX) {
- // If back is committed, progress is the distance between the last and first touch
- // point, divided by the max drag distance. Otherwise, it's the distance between
- // the last touch point and the starting threshold, divided by max drag distance.
- // The starting threshold is initially the first touch location, and updated to
- // the location everytime back is restarted after being cancelled.
- float startX = mTriggerBack ? mInitTouchX : mStartThresholdX;
- float distance;
- if (mSwipeEdge == BackEvent.EDGE_LEFT) {
- distance = touchX - startX;
- } else {
- distance = startX - touchX;
- }
- float deltaX = Math.max(0f, distance);
- float linearDistance = mLinearDistance;
- float maxDistance = getMaxDistance();
- maxDistance = maxDistance == 0 ? 1 : maxDistance;
- float progress;
- if (linearDistance < maxDistance) {
- // Up to linearDistance it behaves linearly, then slowly reaches 1f.
-
- // maxDistance is composed of linearDistance + nonLinearDistance
- float nonLinearDistance = maxDistance - linearDistance;
- float initialTarget = linearDistance + nonLinearDistance * mNonLinearFactor;
-
- boolean isLinear = deltaX <= linearDistance;
- if (isLinear) {
- progress = deltaX / initialTarget;
- } else {
- float nonLinearDeltaX = deltaX - linearDistance;
- float nonLinearProgress = nonLinearDeltaX / nonLinearDistance;
- float currentTarget = MathUtils.lerp(
- /* start = */ initialTarget,
- /* stop = */ maxDistance,
- /* amount = */ nonLinearProgress);
- progress = deltaX / currentTarget;
- }
- } else {
- // Always linear behavior.
- progress = deltaX / maxDistance;
- }
- return MathUtils.constrain(progress, 0, 1);
- }
-
- /**
- * Maximum distance in pixels.
- * Progress is considered to be completed (1f) when this limit is exceeded.
- */
- float getMaxDistance() {
- return mMaxDistance;
- }
-
- BackMotionEvent createProgressEvent(float progress) {
- return new BackMotionEvent(
- /* touchX = */ mLatestTouchX,
- /* touchY = */ mLatestTouchY,
- /* progress = */ progress,
- /* velocityX = */ mLatestVelocityX,
- /* velocityY = */ mLatestVelocityY,
- /* triggerBack = */ mTriggerBack,
- /* swipeEdge = */ mSwipeEdge,
- /* departingAnimationTarget = */ null);
- }
-
- public void setProgressThresholds(float linearDistance, float maxDistance,
- float nonLinearFactor) {
- if (LINEAR_DISTANCE >= 0) {
- mLinearDistance = LINEAR_DISTANCE;
- } else {
- mLinearDistance = linearDistance;
- }
- mMaxDistance = maxDistance;
- mNonLinearFactor = nonLinearFactor;
- }
-
- void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "TouchTracker state:");
- pw.println(prefix + " mState=" + mState);
- pw.println(prefix + " mTriggerBack=" + mTriggerBack);
- }
-
- enum TouchTrackerState {
- INITIAL, ACTIVE, FINISHED
- }
-
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index a67821b7e819..0297901c8921 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -288,13 +288,16 @@ public class BadgedImageView extends ConstraintLayout {
/** Sets the position of the dot and badge, animating them out and back in if requested. */
void animateDotBadgePositions(boolean onLeft) {
- mOnLeft = onLeft;
-
- if (onLeft != getDotOnLeft() && shouldDrawDot()) {
- animateDotScale(0f /* showDot */, () -> {
- invalidate();
- animateDotScale(1.0f, null /* after */);
- });
+ if (onLeft != getDotOnLeft()) {
+ if (shouldDrawDot()) {
+ animateDotScale(0f /* showDot */, () -> {
+ mOnLeft = onLeft;
+ invalidate();
+ animateDotScale(1.0f, null /* after */);
+ });
+ } else {
+ mOnLeft = onLeft;
+ }
}
// TODO animate badge
showBadge();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 74f087b6d8f8..4e8afccee40f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -68,6 +68,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.AlphaOptimizedButton;
import com.android.wm.shell.common.TriangleShape;
@@ -446,6 +447,8 @@ public class BubbleExpandedView extends LinearLayout {
mManageButton.setVisibility(GONE);
} else {
mTaskView = bubbleTaskView.getTaskView();
+ // reset the insets that might left after TaskView is shown in BubbleBarExpandedView
+ mTaskView.setCaptionInsets(null);
bubbleTaskView.setDelegateListener(mTaskViewListener);
// set a fixed width so it is not recalculated as part of a rotation. the width will be
@@ -668,6 +671,11 @@ public class BubbleExpandedView extends LinearLayout {
}
}
+ /** Sets the alpha for the pointer. */
+ public void setPointerAlpha(float alpha) {
+ mPointerView.setAlpha(alpha);
+ }
+
/**
* Get alpha from underlying {@code TaskView} if this view is for a bubble.
* Or get alpha for the overflow view if this view is for overflow.
@@ -698,12 +706,14 @@ public class BubbleExpandedView extends LinearLayout {
}
}
- /**
- * Sets the alpha of the background and the pointer view.
- */
+ /** Sets the alpha of the background. */
public void setBackgroundAlpha(float alpha) {
- mPointerView.setAlpha(alpha);
- setAlpha(alpha);
+ if (Flags.enableNewBubbleAnimations()) {
+ setAlpha(alpha);
+ } else {
+ mPointerView.setAlpha(alpha);
+ setAlpha(alpha);
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 8da85d2d6abf..dcc536b5b043 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -2358,9 +2358,9 @@ public class BubbleStackView extends FrameLayout
} else {
index = getBubbleIndex(mExpandedBubble);
}
- PointF p = mPositioner.getExpandedBubbleXY(index, getState());
+ PointF bubbleXY = mPositioner.getExpandedBubbleXY(index, getState());
final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
- mPositioner.showBubblesVertically() ? p.y : p.x);
+ mPositioner.showBubblesVertically() ? bubbleXY.y : bubbleXY.x);
mExpandedViewContainer.setTranslationX(0f);
mExpandedViewContainer.setTranslationY(translationY);
mExpandedViewContainer.setAlpha(1f);
@@ -2371,40 +2371,42 @@ public class BubbleStackView extends FrameLayout
? mStackAnimationController.getStackPosition().y
: mStackAnimationController.getStackPosition().x;
final float bubbleWillBeAt = showVertically
- ? p.y
- : p.x;
+ ? bubbleXY.y
+ : bubbleXY.x;
final float distanceAnimated = Math.abs(bubbleWillBeAt - relevantStackPosition);
// Wait for the path animation target to reach its end, and add a small amount of extra time
// if the bubble is moving a lot horizontally.
- long startDelay = 0L;
+ final long startDelay;
// Should not happen since we lay out before expanding, but just in case...
if (getWidth() > 0) {
startDelay = (long)
(ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 1.2f
+ (distanceAnimated / getWidth()) * 30);
+ } else {
+ startDelay = 0L;
}
// Set the pivot point for the scale, so the expanded view animates out from the bubble.
if (showVertically) {
float pivotX;
if (mStackOnLeftOrWillBe) {
- pivotX = p.x + mBubbleSize + mExpandedViewPadding;
+ pivotX = bubbleXY.x + mBubbleSize + mExpandedViewPadding;
} else {
- pivotX = p.x - mExpandedViewPadding;
+ pivotX = bubbleXY.x - mExpandedViewPadding;
}
mExpandedViewContainerMatrix.setScale(
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
pivotX,
- p.y + mBubbleSize / 2f);
+ bubbleXY.y + mBubbleSize / 2f);
} else {
mExpandedViewContainerMatrix.setScale(
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
- p.x + mBubbleSize / 2f,
- p.y + mBubbleSize + mExpandedViewPadding);
+ bubbleXY.x + mBubbleSize / 2f,
+ bubbleXY.y + mBubbleSize + mExpandedViewPadding);
}
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index b87c2f6ebad5..7ceaaea3962f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -125,6 +125,7 @@ public class PipBoundsState {
private @Nullable Runnable mOnMinimalSizeChangeCallback;
private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
+ private List<Consumer<Float>> mOnAspectRatioChangedCallbacks = new ArrayList<>();
// the size of the current bounds relative to the max size spec
private float mBoundsScale;
@@ -297,7 +298,12 @@ public class PipBoundsState {
/** Set the PIP aspect ratio. */
public void setAspectRatio(float aspectRatio) {
- mAspectRatio = aspectRatio;
+ if (Float.compare(mAspectRatio, aspectRatio) != 0) {
+ mAspectRatio = aspectRatio;
+ for (Consumer<Float> callback : mOnAspectRatioChangedCallbacks) {
+ callback.accept(mAspectRatio);
+ }
+ }
}
/** Get the PIP aspect ratio. */
@@ -527,6 +533,23 @@ public class PipBoundsState {
mOnPipExclusionBoundsChangeCallbacks.remove(onPipExclusionBoundsChangeCallback);
}
+ /** Adds callback to listen on aspect ratio change. */
+ public void addOnAspectRatioChangedCallback(
+ @NonNull Consumer<Float> onAspectRatioChangedCallback) {
+ if (!mOnAspectRatioChangedCallbacks.contains(onAspectRatioChangedCallback)) {
+ mOnAspectRatioChangedCallbacks.add(onAspectRatioChangedCallback);
+ onAspectRatioChangedCallback.accept(mAspectRatio);
+ }
+ }
+
+ /** Removes callback to listen on aspect ratio change. */
+ public void removeOnAspectRatioChangedCallback(
+ @NonNull Consumer<Float> onAspectRatioChangedCallback) {
+ if (mOnAspectRatioChangedCallbacks.contains(onAspectRatioChangedCallback)) {
+ mOnAspectRatioChangedCallbacks.remove(onAspectRatioChangedCallback);
+ }
+ }
+
public LauncherState getLauncherState() {
return mLauncherState;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index dae62ac74483..30eb8b5d2f05 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.common.split;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -109,7 +110,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
}
/** Inflates split decor surface on the root surface. */
- public void inflate(Context context, SurfaceControl rootLeash, Rect rootBounds) {
+ public void inflate(Context context, SurfaceControl rootLeash) {
if (mIconLeash != null && mViewHost != null) {
return;
}
@@ -128,13 +129,12 @@ public class SplitDecorManager extends WindowlessWindowManager {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
- lp.width = rootBounds.width();
- lp.height = rootBounds.height();
+ lp.width = mIconSize;
+ lp.height = mIconSize;
lp.token = new Binder();
lp.setTitle(TAG);
lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
- // TRUSTED_OVERLAY for windowless window without input channel.
+ lp.inputFeatures |= INPUT_FEATURE_NO_INPUT_CHANNEL;
mViewHost.setView(rootLayout, lp);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 86571cf9c622..5c292f173e5b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -20,7 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AppCompatTaskInfo.CameraCompatControlState;
+import android.app.CameraCompatTaskInfo.CameraCompatControlState;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
index a0986fa601f2..2b0bd3272ed2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
@@ -16,10 +16,10 @@
package com.android.wm.shell.compatui;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import android.annotation.IdRes;
-import android.app.AppCompatTaskInfo.CameraCompatControlState;
+import android.app.CameraCompatTaskInfo.CameraCompatControlState;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index dbf7186def8a..4e5c2fa24f25 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -16,16 +16,16 @@
package com.android.wm.shell.compatui;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppCompatTaskInfo;
-import android.app.AppCompatTaskInfo.CameraCompatControlState;
+import android.app.CameraCompatTaskInfo.CameraCompatControlState;
import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Rect;
@@ -81,7 +81,8 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mCallback = callback;
mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
- mCameraCompatControlState = taskInfo.appCompatTaskInfo.cameraCompatControlState;
+ mCameraCompatControlState =
+ taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState;
mCompatUIHintsState = compatUIHintsState;
mCompatUIConfiguration = compatUIConfiguration;
mOnRestartButtonClicked = onRestartButtonClicked;
@@ -135,7 +136,8 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
final boolean prevHasSizeCompat = mHasSizeCompat;
final int prevCameraCompatControlState = mCameraCompatControlState;
mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
- mCameraCompatControlState = taskInfo.appCompatTaskInfo.cameraCompatControlState;
+ mCameraCompatControlState =
+ taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState;
if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) {
return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index 7c280994042b..8fb4bdbea933 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -237,7 +237,8 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract
final int letterboxWidth = taskInfo.topActivityLetterboxWidth;
// App is not visibly letterboxed if it covers status bar/bottom insets or matches the
// stable bounds, so don't show the button
- if (stableBounds.height() <= letterboxHeight && stableBounds.width() <= letterboxWidth) {
+ if (stableBounds.height() <= letterboxHeight && stableBounds.width() <= letterboxWidth
+ && !taskInfo.isUserFullscreenOverrideEnabled) {
return false;
}
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 73228de83c0f..6834e6d3123f 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
@@ -107,6 +107,7 @@ import com.android.wm.shell.taskview.TaskViewFactory;
import com.android.wm.shell.taskview.TaskViewFactoryController;
import com.android.wm.shell.taskview.TaskViewTransitions;
import com.android.wm.shell.transition.HomeTransitionObserver;
+import com.android.wm.shell.transition.MixedTransitionHandler;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
import com.android.wm.shell.unfold.UnfoldAnimationController;
@@ -675,6 +676,22 @@ public abstract class WMShellBaseModule {
return new TaskViewTransitions(transitions);
}
+ // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract MixedTransitionHandler optionalMixedTransitionHandler();
+
+ @WMSingleton
+ @Provides
+ static Optional<MixedTransitionHandler> provideMixedTransitionHandler(
+ @DynamicOverride Optional<MixedTransitionHandler> mixedTransitionHandler
+ ) {
+ if (mixedTransitionHandler.isPresent()) {
+ return mixedTransitionHandler;
+ }
+ return Optional.empty();
+ }
+
//
// Keyguard transitions (optional feature)
//
@@ -934,6 +951,7 @@ public abstract class WMShellBaseModule {
Optional<OneHandedController> oneHandedControllerOptional,
Optional<HideDisplayCutoutController> hideDisplayCutoutControllerOptional,
Optional<ActivityEmbeddingController> activityEmbeddingOptional,
+ Optional<MixedTransitionHandler> mixedTransitionHandler,
Transitions transitions,
StartingWindowController startingWindow,
ProtoLogController protoLogController,
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 1408eadf544e..b574b8159307 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
@@ -29,6 +29,7 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.IconProvider;
+import com.android.window.flags.Flags;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -59,6 +60,7 @@ import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
import com.android.wm.shell.desktopmode.DesktopTasksTransitionObserver;
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
@@ -85,6 +87,7 @@ import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.taskview.TaskViewTransitions;
import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.HomeTransitionObserver;
+import com.android.wm.shell.transition.MixedTransitionHandler;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
import com.android.wm.shell.unfold.UnfoldAnimationController;
@@ -371,8 +374,9 @@ public abstract class WMShellModule {
//
@WMSingleton
+ @DynamicOverride
@Provides
- static DefaultMixedHandler provideDefaultMixedHandler(
+ static MixedTransitionHandler provideMixedTransitionHandler(
ShellInit shellInit,
Optional<SplitScreenController> splitScreenOptional,
@Nullable PipTransitionController pipTransitionController,
@@ -517,23 +521,39 @@ public abstract class WMShellModule {
LaunchAdjacentController launchAdjacentController,
RecentsTransitionHandler recentsTransitionHandler,
MultiInstanceHelper multiInstanceHelper,
- @ShellMainThread ShellExecutor mainExecutor
- ) {
+ @ShellMainThread ShellExecutor mainExecutor,
+ Optional<DesktopTasksLimiter> desktopTasksLimiter) {
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
dragAndDropController, transitions, enterDesktopTransitionHandler,
exitDesktopTransitionHandler, toggleResizeDesktopTaskTransitionHandler,
dragToDesktopTransitionHandler, desktopModeTaskRepository,
desktopModeLoggerTransitionObserver, launchAdjacentController,
- recentsTransitionHandler, multiInstanceHelper, mainExecutor);
+ recentsTransitionHandler, multiInstanceHelper, mainExecutor, desktopTasksLimiter);
+ }
+
+ @WMSingleton
+ @Provides
+ static Optional<DesktopTasksLimiter> provideDesktopTasksLimiter(
+ Transitions transitions,
+ @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
+ ShellTaskOrganizer shellTaskOrganizer) {
+ if (!DesktopModeStatus.isEnabled() || !Flags.enableDesktopWindowingTaskLimit()) {
+ return Optional.empty();
+ }
+ return Optional.of(
+ new DesktopTasksLimiter(
+ transitions, desktopModeTaskRepository, shellTaskOrganizer));
}
+
@WMSingleton
@Provides
static DragToDesktopTransitionHandler provideDragToDesktopTransitionHandler(
Context context,
Transitions transitions,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ Optional<DesktopTasksLimiter> desktopTasksLimiter) {
return new DragToDesktopTransitionHandler(context, transitions,
rootTaskDisplayAreaOrganizer);
}
@@ -541,7 +561,8 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
static EnterDesktopTaskTransitionHandler provideEnterDesktopModeTaskTransitionHandler(
- Transitions transitions) {
+ Transitions transitions,
+ Optional<DesktopTasksLimiter> desktopTasksLimiter) {
return new EnterDesktopTaskTransitionHandler(transitions);
}
@@ -636,7 +657,6 @@ public abstract class WMShellModule {
@Provides
static Object provideIndependentShellComponentsToCreate(
DragAndDropController dragAndDropController,
- DefaultMixedHandler defaultMixedHandler,
Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) {
return new Object();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
index 32c22c01a828..fcddcad3a949 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -77,6 +77,22 @@ public class DesktopModeStatus {
"persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
/**
+ * Default value for {@code MAX_TASK_LIMIT}.
+ */
+ @VisibleForTesting
+ public static final int DEFAULT_MAX_TASK_LIMIT = 4;
+
+ // TODO(b/335131008): add a config-overlay field for the max number of tasks in Desktop Mode
+ /**
+ * Flag declaring the maximum number of Tasks to show in Desktop Mode at any one time.
+ *
+ * <p> The limit does NOT affect Picture-in-Picture, Bubbles, or System Modals (like a screen
+ * recording window, or Bluetooth pairing window).
+ */
+ private static final int MAX_TASK_LIMIT = SystemProperties.getInt(
+ "persist.wm.debug.desktop_max_task_limit", DEFAULT_MAX_TASK_LIMIT);
+
+ /**
* Return {@code true} if desktop windowing is enabled
*/
public static boolean isEnabled() {
@@ -124,6 +140,13 @@ public class DesktopModeStatus {
}
/**
+ * Return the maximum limit on the number of Tasks to show in Desktop Mode at any one time.
+ */
+ static int getMaxTaskLimit() {
+ return MAX_TASK_LIMIT;
+ }
+
+ /**
* Return {@code true} if the current device supports desktop mode.
*/
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 50cea01fa281..2d508b2e6e3d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -47,6 +47,7 @@ class DesktopModeTaskRepository {
*/
val activeTasks: ArraySet<Int> = ArraySet(),
val visibleTasks: ArraySet<Int> = ArraySet(),
+ val minimizedTasks: ArraySet<Int> = ArraySet(),
var stashed: Boolean = false
)
@@ -202,6 +203,13 @@ class DesktopModeTaskRepository {
}
}
+ /** Return whether the given Task is minimized. */
+ fun isMinimizedTask(taskId: Int): Boolean {
+ return displayData.valueIterator().asSequence().any { data ->
+ data.minimizedTasks.contains(taskId)
+ }
+ }
+
/**
* Check if a task with the given [taskId] is the only active task on its display
*/
@@ -219,6 +227,25 @@ class DesktopModeTaskRepository {
}
/**
+ * Returns whether Desktop Mode is currently showing any tasks, i.e. whether any Desktop Tasks
+ * are visible.
+ */
+ fun isDesktopModeShowing(displayId: Int): Boolean = getVisibleTaskCount(displayId) > 0
+
+ /**
+ * Returns a list of Tasks IDs representing all active non-minimized Tasks on the given display,
+ * ordered from front to back.
+ */
+ fun getActiveNonMinimizedTasksOrderedFrontToBack(displayId: Int): List<Int> {
+ val activeTasks = getActiveTasks(displayId)
+ val allTasksInZOrder = getFreeformTasksInZOrder()
+ return activeTasks
+ // Don't show already minimized Tasks
+ .filter { taskId -> !isMinimizedTask(taskId) }
+ .sortedBy { taskId -> allTasksInZOrder.indexOf(taskId) }
+ }
+
+ /**
* Get a list of freeform tasks, ordered from top-bottom (top at index 0).
*/
// TODO(b/278084491): pass in display id
@@ -255,6 +282,7 @@ class DesktopModeTaskRepository {
val prevCount = getVisibleTaskCount(displayId)
if (visible) {
displayData.getOrCreate(displayId).visibleTasks.add(taskId)
+ unminimizeTask(displayId, taskId)
} else {
displayData[displayId]?.visibleTasks?.remove(taskId)
}
@@ -312,6 +340,24 @@ class DesktopModeTaskRepository {
freeformTasksInZOrder.add(0, taskId)
}
+ /** Mark a Task as minimized. */
+ fun minimizeTask(displayId: Int, taskId: Int) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeTaskRepository: minimize Task: display=%d, task=%d",
+ displayId, taskId)
+ displayData.getOrCreate(displayId).minimizedTasks.add(taskId)
+ }
+
+ /** Mark a Task as non-minimized. */
+ fun unminimizeTask(displayId: Int, taskId: Int) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeTaskRepository: unminimize Task: display=%d, task=%d",
+ displayId, taskId)
+ displayData[displayId]?.minimizedTasks?.remove(taskId)
+ }
+
/**
* Remove the task from the ordered list.
*/
@@ -325,7 +371,7 @@ class DesktopModeTaskRepository {
boundsBeforeMaximizeByTaskId.remove(taskId)
KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
- "DesktopTaskRepo: remaining freeform tasks: " + freeformTasksInZOrder.toDumpString()
+ "DesktopTaskRepo: remaining freeform tasks: %s", freeformTasksInZOrder.toDumpString(),
)
}
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 068661a6a666..0a9e5d0d5345 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
@@ -88,6 +88,7 @@ import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
import com.android.wm.shell.windowdecor.extension.isFreeform
import com.android.wm.shell.windowdecor.extension.isFullscreen
import java.io.PrintWriter
+import java.util.Optional
import java.util.concurrent.Executor
import java.util.function.Consumer
@@ -113,7 +114,8 @@ class DesktopTasksController(
private val launchAdjacentController: LaunchAdjacentController,
private val recentsTransitionHandler: RecentsTransitionHandler,
private val multiInstanceHelper: MultiInstanceHelper,
- @ShellMainThread private val mainExecutor: ShellExecutor
+ @ShellMainThread private val mainExecutor: ShellExecutor,
+ private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler,
DragAndDropController.DragAndDropListener {
@@ -341,11 +343,13 @@ class DesktopTasksController(
)
exitSplitIfApplicable(wct, task)
// Bring other apps to front first
- bringDesktopAppsToFront(task.displayId, wct)
+ val taskToMinimize =
+ bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
addMoveToDesktopChanges(wct, task)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- enterDesktopTaskTransitionHandler.moveToDesktop(wct)
+ val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct)
+ addPendingMinimizeTransition(transition, taskToMinimize)
} else {
shellTaskOrganizer.applyTransaction(wct)
}
@@ -382,10 +386,14 @@ class DesktopTasksController(
)
val wct = WindowContainerTransaction()
exitSplitIfApplicable(wct, taskInfo)
- bringDesktopAppsToFront(taskInfo.displayId, wct)
+ moveHomeTaskToFront(wct)
+ val taskToMinimize =
+ bringDesktopAppsToFrontBeforeShowingNewTask(
+ taskInfo.displayId, wct, taskInfo.taskId)
addMoveToDesktopChanges(wct, taskInfo)
wct.setBounds(taskInfo.token, freeformBounds)
- dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
+ val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
+ transition?.let { addPendingMinimizeTransition(it, taskToMinimize) }
}
/**
@@ -507,8 +515,10 @@ class DesktopTasksController(
val wct = WindowContainerTransaction()
wct.reorder(taskInfo.token, true)
+ val taskToMinimize = addAndGetMinimizeChangesIfNeeded(taskInfo.displayId, wct, taskInfo)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- transitions.startTransition(TRANSIT_TO_FRONT, wct, null /* handler */)
+ val transition = transitions.startTransition(TRANSIT_TO_FRONT, wct, null /* handler */)
+ addPendingMinimizeTransition(transition, taskToMinimize)
} else {
shellTaskOrganizer.applyTransaction(wct)
}
@@ -688,9 +698,20 @@ class DesktopTasksController(
?: WINDOWING_MODE_UNDEFINED
}
- private fun bringDesktopAppsToFront(displayId: Int, wct: WindowContainerTransaction) {
- KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: bringDesktopAppsToFront")
- val activeTasks = desktopModeTaskRepository.getActiveTasks(displayId)
+ private fun bringDesktopAppsToFrontBeforeShowingNewTask(
+ displayId: Int,
+ wct: WindowContainerTransaction,
+ newTaskIdInFront: Int
+ ): RunningTaskInfo? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront)
+
+ private fun bringDesktopAppsToFront(
+ displayId: Int,
+ wct: WindowContainerTransaction,
+ newTaskIdInFront: Int? = null
+ ): RunningTaskInfo? {
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: bringDesktopAppsToFront, newTaskIdInFront=%s",
+ newTaskIdInFront ?: "null")
if (Flags.enableDesktopWindowingWallpaperActivity()) {
// Add translucent wallpaper activity to show the wallpaper underneath
@@ -700,13 +721,21 @@ class DesktopTasksController(
moveHomeTaskToFront(wct)
}
- // Then move other tasks on top of it
- val allTasksInZOrder = desktopModeTaskRepository.getFreeformTasksInZOrder()
- activeTasks
- // Sort descending as the top task is at index 0. It should be ordered to top last
- .sortedByDescending { taskId -> allTasksInZOrder.indexOf(taskId) }
- .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) }
- .forEach { task -> wct.reorder(task.token, true /* onTop */) }
+ val nonMinimizedTasksOrderedFrontToBack =
+ desktopModeTaskRepository.getActiveNonMinimizedTasksOrderedFrontToBack(displayId)
+ // If we're adding a new Task we might need to minimize an old one
+ val taskToMinimize: RunningTaskInfo? =
+ if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) {
+ desktopTasksLimiter.get().getTaskToMinimizeIfNeeded(
+ nonMinimizedTasksOrderedFrontToBack, newTaskIdInFront)
+ } else { null }
+ nonMinimizedTasksOrderedFrontToBack
+ // If there is a Task to minimize, let it stay behind the Home Task
+ .filter { taskId -> taskId != taskToMinimize?.taskId }
+ .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) }
+ .reversed() // Start from the back so the front task is brought forward last
+ .forEach { task -> wct.reorder(task.token, true /* onTop */) }
+ return taskToMinimize
}
private fun moveHomeTaskToFront(wct: WindowContainerTransaction) {
@@ -824,13 +853,13 @@ class DesktopTasksController(
when {
request.type == TRANSIT_TO_BACK -> handleBackNavigation(task)
// If display has tasks stashed, handle as stashed launch
- task.isStashed -> handleStashedTaskLaunch(task)
+ task.isStashed -> handleStashedTaskLaunch(task, transition)
// Check if the task has a top transparent activity
shouldLaunchAsModal(task) -> handleTransparentTaskLaunch(task)
// Check if fullscreen task should be updated
- task.isFullscreen -> handleFullscreenTaskLaunch(task)
+ task.isFullscreen -> handleFullscreenTaskLaunch(task, transition)
// Check if freeform task should be updated
- task.isFreeform -> handleFreeformTaskLaunch(task)
+ task.isFreeform -> handleFreeformTaskLaunch(task, transition)
else -> {
null
}
@@ -878,10 +907,12 @@ class DesktopTasksController(
} ?: false
}
- private fun handleFreeformTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+ private fun handleFreeformTaskLaunch(
+ task: RunningTaskInfo,
+ transition: IBinder
+ ): WindowContainerTransaction? {
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch")
- val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
- if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) {
+ if (!desktopModeTaskRepository.isDesktopModeShowing(task.displayId)) {
KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: switch freeform task to fullscreen oon transition" +
@@ -892,13 +923,23 @@ class DesktopTasksController(
addMoveToFullscreenChanges(wct, task)
}
}
+ // Desktop Mode is showing and we're launching a new Task - we might need to minimize
+ // a Task.
+ val wct = WindowContainerTransaction()
+ val taskToMinimize = addAndGetMinimizeChangesIfNeeded(task.displayId, wct, task)
+ if (taskToMinimize != null) {
+ addPendingMinimizeTransition(transition, taskToMinimize)
+ return wct
+ }
return null
}
- private fun handleFullscreenTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+ private fun handleFullscreenTaskLaunch(
+ task: RunningTaskInfo,
+ transition: IBinder
+ ): WindowContainerTransaction? {
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFullscreenTaskLaunch")
- val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
- if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) {
+ if (desktopModeTaskRepository.isDesktopModeShowing(task.displayId)) {
KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: switch fullscreen task to freeform on transition" +
@@ -907,21 +948,30 @@ class DesktopTasksController(
)
return WindowContainerTransaction().also { wct ->
addMoveToDesktopChanges(wct, task)
+ // Desktop Mode is already showing and we're launching a new Task - we might need to
+ // minimize another Task.
+ val taskToMinimize = addAndGetMinimizeChangesIfNeeded(task.displayId, wct, task)
+ addPendingMinimizeTransition(transition, taskToMinimize)
}
}
return null
}
- private fun handleStashedTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction {
+ private fun handleStashedTaskLaunch(
+ task: RunningTaskInfo,
+ transition: IBinder
+ ): WindowContainerTransaction {
KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: launch apps with stashed on transition taskId=%d",
task.taskId
)
val wct = WindowContainerTransaction()
- bringDesktopAppsToFront(task.displayId, wct)
+ val taskToMinimize =
+ bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
addMoveToDesktopChanges(wct, task)
desktopModeTaskRepository.setStashed(task.displayId, false)
+ addPendingMinimizeTransition(transition, taskToMinimize)
return wct
}
@@ -1002,6 +1052,28 @@ class DesktopTasksController(
wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
}
+ /** Returns the ID of the Task that will be minimized, or null if no task will be minimized. */
+ private fun addAndGetMinimizeChangesIfNeeded(
+ displayId: Int,
+ wct: WindowContainerTransaction,
+ newTaskInfo: RunningTaskInfo
+ ): RunningTaskInfo? {
+ if (!desktopTasksLimiter.isPresent) return null
+ return desktopTasksLimiter.get().addAndGetMinimizeTaskChangesIfNeeded(
+ displayId, wct, newTaskInfo)
+ }
+
+ private fun addPendingMinimizeTransition(
+ transition: IBinder,
+ taskToMinimize: RunningTaskInfo?
+ ) {
+ if (taskToMinimize == null) return
+ desktopTasksLimiter.ifPresent {
+ it.addPendingMinimizeChange(
+ transition, taskToMinimize.displayId, taskToMinimize.taskId)
+ }
+ }
+
/** Enter split by using the focused desktop task in given `displayId`. */
fun enterSplit(
displayId: Int,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
new file mode 100644
index 000000000000..3404d376fe92
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2024 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.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.os.IBinder
+import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_TO_BACK
+import android.window.TransitionInfo
+import android.window.WindowContainerTransaction
+import androidx.annotation.VisibleForTesting
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TransitionObserver
+import com.android.wm.shell.util.KtProtoLog
+
+/**
+ * Limits the number of tasks shown in Desktop Mode.
+ *
+ * This class should only be used if
+ * [com.android.window.flags.Flags.enableDesktopWindowingTaskLimit()] is true.
+ */
+class DesktopTasksLimiter (
+ transitions: Transitions,
+ private val taskRepository: DesktopModeTaskRepository,
+ private val shellTaskOrganizer: ShellTaskOrganizer,
+) {
+ private val minimizeTransitionObserver = MinimizeTransitionObserver()
+
+ init {
+ transitions.registerObserver(minimizeTransitionObserver)
+ }
+
+ private data class TaskDetails (val displayId: Int, val taskId: Int)
+
+ // TODO(b/333018485): replace this observer when implementing the minimize-animation
+ private inner class MinimizeTransitionObserver : TransitionObserver {
+ private val mPendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
+
+ fun addPendingTransitionToken(transition: IBinder, taskDetails: TaskDetails) {
+ mPendingTransitionTokensAndTasks[transition] = taskDetails
+ }
+
+ override fun onTransitionReady(
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: SurfaceControl.Transaction,
+ finishTransaction: SurfaceControl.Transaction
+ ) {
+ val taskToMinimize = mPendingTransitionTokensAndTasks.remove(transition) ?: return
+
+ if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return
+
+ if (!isTaskReorderedToBackOrInvisible(info, taskToMinimize)) {
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksLimiter: task %d is not reordered to back nor invis",
+ taskToMinimize.taskId)
+ return
+ }
+ this@DesktopTasksLimiter.markTaskMinimized(
+ taskToMinimize.displayId, taskToMinimize.taskId)
+ }
+
+ /**
+ * Returns whether the given Task is being reordered to the back in the given transition, or
+ * is already invisible.
+ *
+ * <p> This check can be used to double-check that a task was indeed minimized before
+ * marking it as such.
+ */
+ private fun isTaskReorderedToBackOrInvisible(
+ info: TransitionInfo,
+ taskDetails: TaskDetails
+ ): Boolean {
+ val taskChange = info.changes.find { change ->
+ change.taskInfo?.taskId == taskDetails.taskId }
+ if (taskChange == null) {
+ return !taskRepository.isVisibleTask(taskDetails.taskId)
+ }
+ return taskChange.mode == TRANSIT_TO_BACK
+ }
+
+ override fun onTransitionStarting(transition: IBinder) {}
+
+ override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
+ mPendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
+ mPendingTransitionTokensAndTasks[playing] = taskToTransfer
+ }
+ }
+
+ override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksLimiter: transition %s finished", transition)
+ mPendingTransitionTokensAndTasks.remove(transition)
+ }
+ }
+
+ /**
+ * Mark a task as minimized, this should only be done after the corresponding transition has
+ * finished so we don't minimize the task if the transition fails.
+ */
+ private fun markTaskMinimized(displayId: Int, taskId: Int) {
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksLimiter: marking %d as minimized", taskId)
+ taskRepository.minimizeTask(displayId, taskId)
+ }
+
+ /**
+ * Add a minimize-transition to [wct] if adding [newFrontTaskInfo] brings us over the task
+ * limit.
+ *
+ * @param transition the transition that the minimize-transition will be appended to, or null if
+ * the transition will be started later.
+ * @return the ID of the minimized task, or null if no task is being minimized.
+ */
+ fun addAndGetMinimizeTaskChangesIfNeeded(
+ displayId: Int,
+ wct: WindowContainerTransaction,
+ newFrontTaskInfo: RunningTaskInfo,
+ ): RunningTaskInfo? {
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksLimiter: addMinimizeBackTaskChangesIfNeeded, newFrontTask=%d",
+ newFrontTaskInfo.taskId)
+ val newTaskListOrderedFrontToBack = createOrderedTaskListWithGivenTaskInFront(
+ taskRepository.getActiveNonMinimizedTasksOrderedFrontToBack(displayId),
+ newFrontTaskInfo.taskId)
+ val taskToMinimize = getTaskToMinimizeIfNeeded(newTaskListOrderedFrontToBack)
+ if (taskToMinimize != null) {
+ wct.reorder(taskToMinimize.token, false /* onTop */)
+ return taskToMinimize
+ }
+ return null
+ }
+
+ /**
+ * Add a pending minimize transition change, to update the list of minimized apps once the
+ * transition goes through.
+ */
+ fun addPendingMinimizeChange(transition: IBinder, displayId: Int, taskId: Int) {
+ minimizeTransitionObserver.addPendingTransitionToken(
+ transition, TaskDetails(displayId, taskId))
+ }
+
+ /**
+ * Returns the maximum number of tasks that should ever be displayed at the same time in Desktop
+ * Mode.
+ */
+ fun getMaxTaskLimit(): Int = DesktopModeStatus.getMaxTaskLimit()
+
+ /**
+ * Returns the Task to minimize given 1. a list of visible tasks ordered from front to back and
+ * 2. a new task placed in front of all the others.
+ */
+ fun getTaskToMinimizeIfNeeded(
+ visibleFreeformTaskIdsOrderedFrontToBack: List<Int>,
+ newTaskIdInFront: Int
+ ): RunningTaskInfo? {
+ return getTaskToMinimizeIfNeeded(
+ createOrderedTaskListWithGivenTaskInFront(
+ visibleFreeformTaskIdsOrderedFrontToBack, newTaskIdInFront))
+ }
+
+ /** Returns the Task to minimize given a list of visible tasks ordered from front to back. */
+ fun getTaskToMinimizeIfNeeded(
+ visibleFreeformTaskIdsOrderedFrontToBack: List<Int>
+ ): RunningTaskInfo? {
+ if (visibleFreeformTaskIdsOrderedFrontToBack.size <= getMaxTaskLimit()) {
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksLimiter: no need to minimize; tasks below limit")
+ // No need to minimize anything
+ return null
+ }
+ val taskToMinimize =
+ shellTaskOrganizer.getRunningTaskInfo(
+ visibleFreeformTaskIdsOrderedFrontToBack.last())
+ if (taskToMinimize == null) {
+ KtProtoLog.e(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksLimiter: taskToMinimize == null")
+ return null
+ }
+ return taskToMinimize
+ }
+
+ private fun createOrderedTaskListWithGivenTaskInFront(
+ existingTaskIdsOrderedFrontToBack: List<Int>,
+ newTaskId: Int
+ ): List<Int> {
+ return listOf(newTaskId) +
+ existingTaskIdsOrderedFrontToBack.filter { taskId -> taskId != newTaskId }
+ }
+
+ @VisibleForTesting
+ fun getTransitionObserver(): TransitionObserver {
+ return minimizeTransitionObserver
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 0061d03af8e9..e341f2d4d4b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -150,20 +150,20 @@ class DragToDesktopTransitionHandler(
* windowing mode changes to the dragged task. This is called when the dragged task is released
* inside the desktop drop zone.
*/
- fun finishDragToDesktopTransition(wct: WindowContainerTransaction) {
+ fun finishDragToDesktopTransition(wct: WindowContainerTransaction): IBinder? {
if (!inProgress) {
// Don't attempt to finish a drag to desktop transition since there is no transition in
// progress which means that the drag to desktop transition was never successfully
// started.
- return
+ return null
}
if (requireTransitionState().startAborted) {
// Don't attempt to complete the drag-to-desktop since the start transition didn't
// succeed as expected. Just reset the state as if nothing happened.
clearState()
- return
+ return null
}
- transitions.startTransition(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, wct, this)
+ return transitions.startTransition(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, wct, this)
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 79bb5408df82..74b8f831cdc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -78,10 +78,12 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
/**
* Starts Transition of type TRANSIT_MOVE_TO_DESKTOP
* @param wct WindowContainerTransaction for transition
+ * @return the token representing the started transition
*/
- public void moveToDesktop(@NonNull WindowContainerTransaction wct) {
+ public IBinder moveToDesktop(@NonNull WindowContainerTransaction wct) {
final IBinder token = mTransitions.startTransition(TRANSIT_MOVE_TO_DESKTOP, wct, this);
mPendingTransitionTokens.add(token);
+ return token;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index f2bdcae31956..6fea2036dbd1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -95,6 +95,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
if (DesktopModeStatus.isEnabled()) {
mDesktopModeTaskRepository.ifPresent(repository -> {
repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
+ repository.unminimizeTask(taskInfo.displayId, taskInfo.taskId);
if (taskInfo.isVisible) {
if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
@@ -116,6 +117,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
if (DesktopModeStatus.isEnabled()) {
mDesktopModeTaskRepository.ifPresent(repository -> {
repository.removeFreeformTask(taskInfo.taskId);
+ repository.unminimizeTask(taskInfo.displayId, taskInfo.taskId);
if (repository.removeActiveTask(taskInfo.taskId)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Removing active freeform task: #%d", taskInfo.taskId);
@@ -162,6 +164,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
if (DesktopModeStatus.isEnabled() && taskInfo.isFocused) {
mDesktopModeTaskRepository.ifPresent(repository -> {
repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
+ repository.unminimizeTask(taskInfo.displayId, taskInfo.taskId);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
index ec09827fa4d1..afddfab99a2b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
@@ -1,3 +1,2 @@
# WM shell sub-module pip owner
hwwang@google.com
-mateuszc@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 4c477373c32c..eb845db409e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -743,11 +743,6 @@ public class PipAnimationController {
.alpha(tx, leash, 1f)
.round(tx, leash, shouldApplyCornerRadius())
.shadow(tx, leash, shouldApplyShadowRadius());
- // TODO(b/178632364): this is a work around for the black background when
- // entering PiP in button navigation mode.
- if (isInPipDirection(direction)) {
- tx.setWindowCrop(leash, getStartValue());
- }
tx.show(leash);
tx.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 57cf99211b6e..e885262658f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -597,6 +597,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
+ if (mPipTransitionState.isEnteringPip()
+ && !mPipTransitionState.getInSwipePipToHomeTransition()) {
+ // If we are still entering PiP with Shell playing enter animation, jump-cut to
+ // the end of the enter animation and reschedule exitPip to run after enter-PiP
+ // has finished its transition and allowed the client to draw in PiP mode.
+ mPipTransitionController.end(() -> {
+ exitPip(animationDurationMs, requestEnterSplit);
+ });
+ return;
+ }
+
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"exitPip: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState);
final WindowContainerTransaction wct = new WindowContainerTransaction();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index d60f5a631044..fdde3ee01264 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -43,7 +43,6 @@ import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
-import android.animation.Animator;
import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.TaskInfo;
@@ -348,9 +347,16 @@ public class PipTransition extends PipTransitionController {
@Override
public void end() {
- Animator animator = mPipAnimationController.getCurrentAnimator();
- if (animator != null && animator.isRunning()) {
- animator.end();
+ end(null);
+ }
+
+ @Override
+ public void end(@Nullable Runnable onTransitionEnd) {
+ if (mPipAnimationController.isAnimating()) {
+ mPipAnimationController.getCurrentAnimator().end();
+ }
+ if (onTransitionEnd != null) {
+ onTransitionEnd.run();
}
}
@@ -818,8 +824,13 @@ public class PipTransition extends PipTransitionController {
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull TaskInfo taskInfo) {
startTransaction.apply();
- finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
- mPipDisplayLayoutState.getDisplayBounds());
+ if (info.getChanges().isEmpty()) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "removePipImmediately is called with empty changes");
+ } else {
+ finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
+ mPipDisplayLayoutState.getDisplayBounds());
+ }
mPipOrganizer.onExitPipFinished(taskInfo);
finishCallback.onTransitionFinished(null);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 32442f740a52..4f71a02528c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -305,6 +305,14 @@ public abstract class PipTransitionController implements Transitions.TransitionH
public void end() {
}
+ /**
+ * End the currently-playing PiP animation.
+ *
+ * @param onTransitionEnd callback to run upon finishing the playing transition.
+ */
+ public void end(@Nullable Runnable onTransitionEnd) {
+ }
+
/** Starts the {@link android.window.SystemPerformanceHinter.HighPerfSession}. */
public void startHighPerfSession() {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index c1adfffce074..d8ac8e948a97 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -219,6 +219,7 @@ public class PipTouchHandler {
mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
this::onAccessibilityShowMenu, this::updateMovementBounds,
this::animateToUnStashedState, mainExecutor);
+ mPipBoundsState.addOnAspectRatioChangedCallback(this::updateMinMaxSize);
// TODO(b/181599115): This should really be initializes as part of the pip controller, but
// until all PIP implementations derive from the controller, just initialize the touch handler
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
index 6dabb3bf6f9a..79d1793819f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
@@ -1,4 +1,3 @@
# WM shell sub-module pip owner
hwwang@google.com
-mateuszc@google.com
gabiyev@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index cc8e3e0934e6..472003cb435f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -208,6 +208,7 @@ public class PipTouchHandler {
new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
mTouchState, this::updateMovementBounds, pipUiEventLogger,
menuController, mainExecutor, mPipPerfHintController);
+ mPipBoundsState.addOnAspectRatioChangedCallback(this::updateMinMaxSize);
if (PipUtils.isPip2ExperimentEnabled()) {
shellInit.addInitCallback(this::onInit, this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index c625b69deac0..a7829c905c69 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -27,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION;
import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
import android.annotation.Nullable;
@@ -54,6 +55,7 @@ import android.window.PictureInPictureSurfaceTransaction;
import android.window.TaskSnapshot;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowAnimationState;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -283,6 +285,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
private IBinder mTransition = null;
private boolean mKeyguardLocked = false;
private boolean mWillFinishToHome = false;
+ private Transitions.TransitionHandler mTakeoverHandler = null;
/** The animation is idle, waiting for the user to choose a task to switch to. */
private static final int STATE_NORMAL = 0;
@@ -576,9 +579,13 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"Applying transaction=%d", t.getId());
t.apply();
- Bundle b = new Bundle(1 /*capacity*/);
+
+ mTakeoverHandler = mTransitions.getHandlerForTakeover(mTransition, info);
+
+ Bundle b = new Bundle(2 /*capacity*/);
b.putParcelable(KEY_EXTRA_SPLIT_BOUNDS,
mRecentTasksController.getSplitBoundsForTaskId(closingSplitTaskId));
+ b.putBoolean(KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION, mTakeoverHandler != null);
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"[%d] RecentsController.start: calling onAnimationStart with %d apps",
@@ -597,6 +604,63 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
return true;
}
+ @Override
+ public void handOffAnimation(
+ RemoteAnimationTarget[] targets, WindowAnimationState[] states) {
+ mExecutor.execute(() -> {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.handOffAnimation", mInstanceId);
+
+ if (mTakeoverHandler == null) {
+ Slog.e(TAG, "Tried to hand off an animation without a valid takeover "
+ + "handler.");
+ return;
+ }
+
+ if (targets.length != states.length) {
+ Slog.e(TAG, "Tried to hand off an animation, but the number of targets "
+ + "(" + targets.length + ") doesn't match the number of states "
+ + "(" + states.length + ")");
+ return;
+ }
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.handOffAnimation: got %d states for %d "
+ + "changes", mInstanceId, states.length, mInfo.getChanges().size());
+ WindowAnimationState[] updatedStates =
+ new WindowAnimationState[mInfo.getChanges().size()];
+
+ // Ensure that the ordering of animation states is the same as that of matching
+ // changes in mInfo. prefixOrderIndex is set up in reverse order to that of the
+ // changes, so that's what we use to get to the correct ordering.
+ for (int i = 0; i < targets.length; i++) {
+ RemoteAnimationTarget target = targets[i];
+ updatedStates[updatedStates.length - target.prefixOrderIndex] = states[i];
+ }
+
+ Transitions.TransitionFinishCallback finishCB = mFinishCB;
+ // Reset the callback here, so any stray calls that aren't coming from the new
+ // handler are ignored.
+ mFinishCB = null;
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.handOffAnimation: calling "
+ + "takeOverAnimation with %d states", mInstanceId,
+ updatedStates.length);
+ mTakeoverHandler.takeOverAnimation(
+ mTransition, mInfo, new SurfaceControl.Transaction(),
+ wct -> {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.handOffAnimation: finish "
+ + "callback", mInstanceId);
+ // Set the callback once again so we can finish correctly.
+ mFinishCB = finishCB;
+ finishInner(true /* toHome */, false /* userLeave */,
+ null /* finishCb */);
+ }, updatedStates);
+ });
+ }
+
/**
* Updates this controller when a new transition is requested mid-recents transition.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index fadc9706af6a..4c68106af1fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1762,10 +1762,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "finishEnterSplitScreen");
mSplitLayout.update(finishT, true /* resetImePosition */);
- mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash,
- getMainStageBounds());
- mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash,
- getSideStageBounds());
+ mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash);
+ mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash);
setDividerVisibility(true, finishT);
// Ensure divider surface are re-parented back into the hierarchy at the end of the
// transition. See Transition#buildFinishTransaction for more detail.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index f33ab33dafcc..f41bca36bb70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -218,8 +218,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
// Inflates split decor view only when the root task is visible.
if (!ENABLE_SHELL_TRANSITIONS && mRootTaskInfo.isVisible != taskInfo.isVisible) {
if (taskInfo.isVisible) {
- mSplitDecorManager.inflate(mContext, mRootLeash,
- taskInfo.configuration.windowConfiguration.getBounds());
+ mSplitDecorManager.inflate(mContext, mRootLeash);
} else {
mSyncQueue.runInSync(t -> mSplitDecorManager.release(t));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
index da3aa4adc42c..e552e6cdacf3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
@@ -30,7 +30,6 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
@@ -54,7 +53,6 @@ import android.window.SplashScreenView;
import android.window.StartingWindowInfo;
import android.window.StartingWindowRemovalInfo;
-import com.android.internal.R;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ContrastColorUtil;
import com.android.wm.shell.common.ShellExecutor;
@@ -206,7 +204,6 @@ class SplashscreenWindowCreator extends AbsSplashWindowCreator {
final SplashWindowRecord record =
(SplashWindowRecord) mStartingWindowRecordManager.getRecord(taskId);
if (record != null) {
- record.parseAppSystemBarColor(context);
// Block until we get the background color.
final SplashScreenView contentView = viewSupplier.get();
if (suggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
@@ -427,8 +424,6 @@ class SplashscreenWindowCreator extends AbsSplashWindowCreator {
private boolean mSetSplashScreen;
private SplashScreenView mSplashView;
- private int mSystemBarAppearance;
- private boolean mDrawsSystemBarBackgrounds;
SplashWindowRecord(IBinder appToken, View decorView,
@StartingWindowInfo.StartingWindowType int suggestType) {
@@ -448,19 +443,6 @@ class SplashscreenWindowCreator extends AbsSplashWindowCreator {
mSetSplashScreen = true;
}
- void parseAppSystemBarColor(Context context) {
- final TypedArray a = context.obtainStyledAttributes(R.styleable.Window);
- mDrawsSystemBarBackgrounds = a.getBoolean(
- R.styleable.Window_windowDrawsSystemBarBackgrounds, false);
- if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
- mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
- }
- if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
- mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
- }
- a.recycle();
- }
-
@Override
public boolean removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
if (mRootView == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java
index 56c0d0e67cab..c886cc999216 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java
@@ -42,4 +42,7 @@ public class ShellSharedConstants {
public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode";
// See IDragAndDrop.aidl
public static final String KEY_EXTRA_SHELL_DRAG_AND_DROP = "extra_shell_drag_and_drop";
+ // See IRecentsAnimationController.aidl
+ public static final String KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION =
+ "extra_shell_can_hand_off_animation";
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index 35a1fa0a92f6..a85188a9e04d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -30,7 +30,6 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Handler;
-import android.os.Looper;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -121,6 +120,11 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ if (mTaskViewTaskController.isUsingShellTransitions()) {
+ // No need for additional work as it is already taken care of during
+ // prepareOpenAnimation().
+ return;
+ }
onLocationChanged();
if (taskInfo.taskDescription != null) {
final int bgColor = taskInfo.taskDescription.getBackgroundColor();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 196e04edbb10..11aa402aa283 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.taskview;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -48,6 +49,23 @@ import java.util.concurrent.Executor;
* TaskView} to {@link TaskViewTaskController} interactions are done via direct method calls.
*
* The reverse communication is done via the {@link TaskViewBase} interface.
+ *
+ * <ul>
+ * <li>The entry point for an activity based task view is {@link
+ * TaskViewTaskController#startActivity(PendingIntent, Intent, ActivityOptions, Rect)}</li>
+ *
+ * <li>The entry point for an activity (represented by {@link ShortcutInfo}) based task view
+ * is {@link TaskViewTaskController#startShortcutActivity(ShortcutInfo, ActivityOptions, Rect)}
+ * </li>
+ *
+ * <li>The entry point for a root-task based task view is {@link
+ * TaskViewTaskController#startRootTask(ActivityManager.RunningTaskInfo, SurfaceControl,
+ * WindowContainerTransaction)}.
+ * This method is special as it doesn't create a root task and instead expects that the
+ * launch root task is already created and started. This method just attaches the taskInfo to
+ * the TaskView.
+ * </li>
+ * </ul>
*/
public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
@@ -155,8 +173,8 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
* <p>The owner of this container must be allowed to access the shortcut information,
* as defined in {@link LauncherApps#hasShortcutHostPermission()} to use this method.
*
- * @param shortcut the shortcut used to launch the activity.
- * @param options options for the activity.
+ * @param shortcut the shortcut used to launch the activity.
+ * @param options options for the activity.
* @param launchBounds the bounds (window size and position) that the activity should be
* launched in, in pixels and in screen coordinates.
*/
@@ -183,10 +201,10 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
* Launch a new activity.
*
* @param pendingIntent Intent used to launch an activity.
- * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
- * @param options options for the activity.
- * @param launchBounds the bounds (window size and position) that the activity should be
- * launched in, in pixels and in screen coordinates.
+ * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
+ * @param options options for the activity.
+ * @param launchBounds the bounds (window size and position) that the activity should be
+ * launched in, in pixels and in screen coordinates.
*/
public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
@NonNull ActivityOptions options, @Nullable Rect launchBounds) {
@@ -208,6 +226,35 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
}
}
+
+ /**
+ * Attaches the given root task {@code taskInfo} in the task view.
+ *
+ * <p> Since {@link ShellTaskOrganizer#createRootTask(int, int,
+ * ShellTaskOrganizer.TaskListener)} does not use the shell transitions flow, this method is
+ * used as an entry point for an already-created root-task in the task view.
+ *
+ * @param taskInfo the task info of the root task.
+ * @param leash the {@link android.content.pm.ShortcutInfo.Surface} of the root task
+ * @param wct The Window container work that should happen as part of this set up.
+ */
+ public void startRootTask(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
+ @Nullable WindowContainerTransaction wct) {
+ if (wct == null) {
+ wct = new WindowContainerTransaction();
+ }
+ // This method skips the regular flow where an activity task is launched as part of a new
+ // transition in taskview and then transition is intercepted using the launchcookie.
+ // The task here is already created and running, it just needs to be reparented, resized
+ // and tracked correctly inside taskview. Which is done by calling
+ // prepareOpenAnimationInternal() and then manually enqueuing the resulting window container
+ // transaction.
+ prepareOpenAnimationInternal(true /* newTask */, mTransaction /* startTransaction */,
+ null /* finishTransaction */, taskInfo, leash, wct);
+ mTransaction.apply();
+ mTaskViewTransitions.startInstantTransition(TRANSIT_CHANGE, wct);
+ }
+
private void prepareActivityOptions(ActivityOptions options, Rect launchBounds) {
final Binder launchCookie = new Binder();
mShellExecutor.execute(() -> {
@@ -342,7 +389,6 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
final SurfaceControl taskLeash = mTaskLeash;
handleAndNotifyTaskRemoval(mTaskInfo);
- // Unparent the task when this surface is destroyed
mTransaction.reparent(taskLeash, null).apply();
resetTaskInfo();
}
@@ -597,6 +643,15 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
@NonNull SurfaceControl.Transaction finishTransaction,
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
WindowContainerTransaction wct) {
+ prepareOpenAnimationInternal(newTask, startTransaction, finishTransaction, taskInfo, leash,
+ wct);
+ }
+
+ private void prepareOpenAnimationInternal(final boolean newTask,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
+ ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
+ WindowContainerTransaction wct) {
mPendingInfo = null;
mTaskInfo = taskInfo;
mTaskToken = mTaskInfo.token;
@@ -608,10 +663,12 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
// Also reparent on finishTransaction since the finishTransaction will reparent back
// to its "original" parent by default.
Rect boundsOnScreen = mTaskViewBase.getCurrentBoundsOnScreen();
- finishTransaction.reparent(mTaskLeash, mSurfaceControl)
- .setPosition(mTaskLeash, 0, 0)
- // TODO: maybe once b/280900002 is fixed this will be unnecessary
- .setWindowCrop(mTaskLeash, boundsOnScreen.width(), boundsOnScreen.height());
+ if (finishTransaction != null) {
+ finishTransaction.reparent(mTaskLeash, mSurfaceControl)
+ .setPosition(mTaskLeash, 0, 0)
+ // TODO: maybe once b/280900002 is fixed this will be unnecessary
+ .setWindowCrop(mTaskLeash, boundsOnScreen.width(), boundsOnScreen.height());
+ }
mTaskViewTransitions.updateBoundsState(this, boundsOnScreen);
mTaskViewTransitions.updateVisibilityState(this, true /* visible */);
wct.setBounds(mTaskToken, boundsOnScreen);
@@ -632,6 +689,7 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
mTaskViewBase.setResizeBgColor(startTransaction, backgroundColor);
}
+ mTaskViewBase.onTaskAppeared(mTaskInfo, mTaskLeash);
if (mListener != null) {
final int taskId = mTaskInfo.taskId;
final ComponentName baseActivity = mTaskInfo.baseActivity;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 198ec82b5f21..e6d1b4593a46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -53,7 +53,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
new ArrayMap<>();
private final ArrayList<PendingTransition> mPending = new ArrayList<>();
private final Transitions mTransitions;
- private final boolean[] mRegistered = new boolean[]{ false };
+ private final boolean[] mRegistered = new boolean[]{false};
/**
* TaskView makes heavy use of startTransition. Only one shell-initiated transition can be
@@ -122,6 +122,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
/**
* Looks through the pending transitions for a closing transaction that matches the provided
* `taskView`.
+ *
* @param taskView the pending transition should be for this.
*/
private PendingTransition findPendingCloseTransition(TaskViewTaskController taskView) {
@@ -135,8 +136,17 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
}
/**
+ * Starts a transition outside of the handler associated with {@link TaskViewTransitions}.
+ */
+ public void startInstantTransition(@WindowManager.TransitionType int type,
+ WindowContainerTransaction wct) {
+ mTransitions.startTransition(type, wct, null);
+ }
+
+ /**
* Looks through the pending transitions for a opening transaction that matches the provided
* `taskView`.
+ *
* @param taskView the pending transition should be for this.
*/
@VisibleForTesting
@@ -152,8 +162,9 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
/**
* Looks through the pending transitions for one matching `taskView`.
+ *
* @param taskView the pending transition should be for this.
- * @param type the type of transition it's looking for
+ * @param type the type of transition it's looking for
*/
PendingTransition findPending(TaskViewTaskController taskView, int type) {
for (int i = mPending.size() - 1; i >= 0; --i) {
@@ -220,7 +231,24 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
startNextTransition();
}
- void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
+ /** Starts a new transition to make the given {@code taskView} visible. */
+ public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
+ setTaskViewVisible(taskView, visible, false /* reorder */);
+ }
+
+ /**
+ * Starts a new transition to make the given {@code taskView} visible and optionally change
+ * the task order.
+ *
+ * @param taskView the task view which the visibility is being changed for
+ * @param visible the new visibility of the task view
+ * @param reorder whether to reorder the task or not. If this is {@code true}, the task will be
+ * reordered as per the given {@code visible}. For {@code visible = true}, task
+ * will be reordered to top. For {@code visible = false}, task will be reordered
+ * to the bottom
+ */
+ public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible,
+ boolean reorder) {
if (mTaskViews.get(taskView) == null) return;
if (mTaskViews.get(taskView).mVisible == visible) return;
if (taskView.getTaskInfo() == null) {
@@ -231,6 +259,9 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setHidden(taskView.getTaskInfo().token, !visible /* hidden */);
wct.setBounds(taskView.getTaskInfo().token, mTaskViews.get(taskView).mBounds);
+ if (reorder) {
+ wct.reorder(taskView.getTaskInfo().token, visible /* onTop */);
+ }
PendingTransition pending = new PendingTransition(
visible ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK, wct, taskView, null /* cookie */);
mPending.add(pending);
@@ -238,6 +269,22 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
// visibility is reported in transition.
}
+ /** Starts a new transition to reorder the given {@code taskView}'s task. */
+ public void reorderTaskViewTask(TaskViewTaskController taskView, boolean onTop) {
+ if (mTaskViews.get(taskView) == null) return;
+ if (taskView.getTaskInfo() == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reorder(taskView.getTaskInfo().token, onTop /* onTop */);
+ PendingTransition pending = new PendingTransition(
+ onTop ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK, wct, taskView, null /* cookie */);
+ mPending.add(pending);
+ startNextTransition();
+ // visibility is reported in transition.
+ }
+
void updateBoundsState(TaskViewTaskController taskView, Rect boundsOnScreen) {
TaskViewRequestedState state = mTaskViews.get(taskView);
if (state == null) return;
@@ -380,7 +427,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
}
startTransaction.reparent(chg.getLeash(), tv.getSurfaceControl());
finishTransaction.reparent(chg.getLeash(), tv.getSurfaceControl())
- .setPosition(chg.getLeash(), 0, 0);
+ .setPosition(chg.getLeash(), 0, 0);
changesHandled++;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 8746b8c8d55c..4bc0dc00175b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -61,9 +61,10 @@ import java.util.function.Consumer;
/**
* A handler for dealing with transitions involving multiple other handlers. For example: an
- * activity in split-screen going into PiP.
+ * activity in split-screen going into PiP. Note this is provided as a handset-specific
+ * implementation of {@code MixedTransitionHandler}.
*/
-public class DefaultMixedHandler implements Transitions.TransitionHandler,
+public class DefaultMixedHandler implements MixedTransitionHandler,
RecentsTransitionHandler.RecentsMixedHandler {
private final Transitions mPlayer;
@@ -116,7 +117,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
final IBinder mTransition;
protected final Transitions mPlayer;
- protected final DefaultMixedHandler mMixedHandler;
+ protected final MixedTransitionHandler mMixedHandler;
protected final PipTransitionController mPipHandler;
protected final StageCoordinator mSplitHandler;
protected final KeyguardTransitionHandler mKeyguardHandler;
@@ -142,7 +143,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
int mInFlightSubAnimations = 0;
MixedTransition(int type, IBinder transition, Transitions player,
- DefaultMixedHandler mixedHandler, PipTransitionController pipHandler,
+ MixedTransitionHandler mixedHandler, PipTransitionController pipHandler,
StageCoordinator splitHandler, KeyguardTransitionHandler keyguardHandler) {
mType = type;
mTransition = transition;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index e9cd73b0df5e..b028bd65b438 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -41,7 +41,7 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
private final ActivityEmbeddingController mActivityEmbeddingController;
DefaultMixedTransition(int type, IBinder transition, Transitions player,
- DefaultMixedHandler mixedHandler, PipTransitionController pipHandler,
+ MixedTransitionHandler mixedHandler, PipTransitionController pipHandler,
StageCoordinator splitHandler, KeyguardTransitionHandler keyguardHandler,
UnfoldTransitionHandler unfoldHandler,
ActivityEmbeddingController activityEmbeddingController) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 9adb67c8a65e..2d6ba6ee7217 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -594,7 +594,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
.setName("animation-background")
.setCallsite("DefaultTransitionHandler")
.setColorLayer();
- final SurfaceControl backgroundSurface = colorLayerBuilder.build();
// Attaching the background surface to the transition root could unexpectedly make it
// cover one of the split root tasks. To avoid this, put the background surface just
@@ -605,8 +604,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
if (isSplitTaskInvolved) {
mRootTDAOrganizer.attachToDisplayArea(displayId, colorLayerBuilder);
} else {
- startTransaction.reparent(backgroundSurface, info.getRootLeash());
+ colorLayerBuilder.setParent(info.getRootLeash());
}
+
+ final SurfaceControl backgroundSurface = colorLayerBuilder.build();
startTransaction.setColor(backgroundSurface, colorArray)
.setLayer(backgroundSurface, -1)
.show(backgroundSurface);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHandler.java
new file mode 100644
index 000000000000..ff429fb12c94
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHandler.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 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.transition;
+
+/**
+ * Interface for a {@link Transitions.TransitionHandler} that can take the subset of transitions
+ * that it handles and further decompose those transitions into sub-transitions which can be
+ * independently delegated to separate handlers.
+ */
+public interface MixedTransitionHandler extends Transitions.TransitionHandler {
+
+ // TODO(b/335685449) this currently exists purely as a marker interface for use in form-factor
+ // specific/sysui dagger modules. Going forward, we should define this in a meaningful
+ // way so as to provide a clear basis for expectations/behaviours associated with mixed
+ // transitions and their default handlers.
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
index 0974cd13f249..ffc0b76b131d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
@@ -44,7 +44,7 @@ public class MixedTransitionHelper {
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback,
- @NonNull Transitions player, @NonNull DefaultMixedHandler mixedHandler,
+ @NonNull Transitions player, @NonNull MixedTransitionHandler mixedHandler,
@NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+ "entering PIP while Split-Screen is foreground.");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 94519a0d118c..69c41675e989 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -27,6 +27,7 @@ import android.window.IRemoteTransitionFinishedCallback;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowAnimationState;
import android.window.WindowContainerTransaction;
import com.android.internal.protolog.common.ProtoLog;
@@ -65,30 +66,9 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
+ " transition %s for (#%d).", mRemote, info.getDebugId());
- final IBinder.DeathRecipient remoteDied = () -> {
- Log.e(Transitions.TAG, "Remote transition died, finishing");
- mMainExecutor.execute(
- () -> finishCallback.onTransitionFinished(null /* wct */));
- };
- IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
- @Override
- public void onTransitionFinished(WindowContainerTransaction wct,
- SurfaceControl.Transaction sct) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Finished one-shot remote transition %s for (#%d).", mRemote,
- info.getDebugId());
- if (mRemote.asBinder() != null) {
- mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
- }
- if (sct != null) {
- finishTransaction.merge(sct);
- }
- mMainExecutor.execute(() -> {
- finishCallback.onTransitionFinished(wct);
- mRemote = null;
- });
- }
- };
+ final IBinder.DeathRecipient remoteDied = createDeathRecipient(finishCallback);
+ IRemoteTransitionFinishedCallback cb =
+ createFinishedCallback(info, finishTransaction, finishCallback, remoteDied);
Transitions.setRunningRemoteTransitionDelegate(mRemote.getAppThread());
try {
if (mRemote.asBinder() != null) {
@@ -152,6 +132,51 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
}
@Override
+ public boolean takeOverAnimation(
+ @NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction transaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull WindowAnimationState[] states) {
+ if (mTransition != transition) return false;
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "Using registered One-shot "
+ + "remote transition %s to take over (#%d).", mRemote, info.getDebugId());
+
+ final IBinder.DeathRecipient remoteDied = createDeathRecipient(finishCallback);
+ IRemoteTransitionFinishedCallback cb = createFinishedCallback(
+ info, null /* finishTransaction */, finishCallback, remoteDied);
+
+ Transitions.setRunningRemoteTransitionDelegate(mRemote.getAppThread());
+
+ try {
+ if (mRemote.asBinder() != null) {
+ mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
+ }
+
+ // If the remote is actually in the same process, then make a copy of parameters since
+ // remote impls assume that they have to clean-up native references.
+ final SurfaceControl.Transaction remoteStartT =
+ RemoteTransitionHandler.copyIfLocal(transaction, mRemote.getRemoteTransition());
+ final TransitionInfo remoteInfo =
+ remoteStartT == transaction ? info : info.localRemoteCopy();
+ mRemote.getRemoteTransition().takeOverAnimation(
+ transition, remoteInfo, remoteStartT, cb, states);
+
+ // Assume that remote will apply the transaction.
+ transaction.clear();
+ return true;
+ } catch (RemoteException e) {
+ Log.e(Transitions.TAG, "Error running remote transition takeover.", e);
+ if (mRemote.asBinder() != null) {
+ mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
+ }
+ finishCallback.onTransitionFinished(null /* wct */);
+ mRemote = null;
+ }
+
+ return false;
+ }
+
+ @Override
@Nullable
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@Nullable TransitionRequestInfo request) {
@@ -174,6 +199,41 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
}
}
+ private IBinder.DeathRecipient createDeathRecipient(
+ Transitions.TransitionFinishCallback finishCallback) {
+ return () -> {
+ Log.e(Transitions.TAG, "Remote transition died, finishing");
+ mMainExecutor.execute(
+ () -> finishCallback.onTransitionFinished(null /* wct */));
+ };
+ }
+
+ private IRemoteTransitionFinishedCallback createFinishedCallback(
+ @NonNull TransitionInfo info,
+ @Nullable SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull IBinder.DeathRecipient remoteDied) {
+ return new IRemoteTransitionFinishedCallback.Stub() {
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Finished one-shot remote transition %s for (#%d).", mRemote,
+ info.getDebugId());
+ if (mRemote.asBinder() != null) {
+ mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
+ }
+ if (finishTransaction != null && sct != null) {
+ finishTransaction.merge(sct);
+ }
+ mMainExecutor.execute(() -> {
+ finishCallback.onTransitionFinished(wct);
+ mRemote = null;
+ });
+ }
+ };
+ }
+
@Override
public String toString() {
return "OneShotRemoteHandler:" + mRemote.getDebugName() + ":"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index 5b402a5a7d53..d6e64cfaf4d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -43,7 +43,7 @@ class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition {
private final DesktopTasksController mDesktopTasksController;
RecentsMixedTransition(int type, IBinder transition, Transitions player,
- DefaultMixedHandler mixedHandler, PipTransitionController pipHandler,
+ MixedTransitionHandler mixedHandler, PipTransitionController pipHandler,
StageCoordinator splitHandler, KeyguardTransitionHandler keyguardHandler,
RecentsTransitionHandler recentsHandler,
DesktopTasksController desktopTasksController) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 4c4c5806ea55..d6860464d055 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.transition;
+import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
@@ -32,6 +34,7 @@ import android.window.RemoteTransition;
import android.window.TransitionFilter;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowAnimationState;
import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
@@ -41,7 +44,9 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransitionUtil;
+import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* Handler that deals with RemoteTransitions. It will only request to handle a transition
@@ -58,6 +63,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
/** Ordered by specificity. Last filters will be checked first */
private final ArrayList<Pair<TransitionFilter, RemoteTransition>> mFilters =
new ArrayList<>();
+ private final ArrayList<Pair<TransitionFilter, RemoteTransition>> mTakeoverFilters =
+ new ArrayList<>();
private final ArrayMap<IBinder, RemoteDeathHandler> mDeathHandlers = new ArrayMap<>();
@@ -70,14 +77,23 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
mFilters.add(new Pair<>(filter, remote));
}
+ void addFilteredForTakeover(TransitionFilter filter, RemoteTransition remote) {
+ handleDeath(remote.asBinder(), null /* finishCallback */);
+ mTakeoverFilters.add(new Pair<>(filter, remote));
+ }
+
void removeFiltered(RemoteTransition remote) {
boolean removed = false;
- for (int i = mFilters.size() - 1; i >= 0; --i) {
- if (mFilters.get(i).second.asBinder().equals(remote.asBinder())) {
- mFilters.remove(i);
- removed = true;
+ for (ArrayList<Pair<TransitionFilter, RemoteTransition>> filters
+ : Arrays.asList(mFilters, mTakeoverFilters)) {
+ for (int i = filters.size() - 1; i >= 0; --i) {
+ if (filters.get(i).second.asBinder().equals(remote.asBinder())) {
+ filters.remove(i);
+ removed = true;
+ }
}
}
+
if (removed) {
unhandleDeath(remote.asBinder(), null /* finishCallback */);
}
@@ -237,6 +253,47 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
}
}
+ @Nullable
+ @Override
+ public Transitions.TransitionHandler getHandlerForTakeover(
+ @NonNull IBinder transition, @NonNull TransitionInfo info) {
+ if (!returnAnimationFrameworkLibrary()) {
+ return null;
+ }
+
+ for (Pair<TransitionFilter, RemoteTransition> registered : mTakeoverFilters) {
+ if (registered.first.matches(info)) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "Found matching remote to takeover (#%d)", info.getDebugId());
+
+ OneShotRemoteHandler oneShot =
+ new OneShotRemoteHandler(mMainExecutor, registered.second);
+ oneShot.setTransition(transition);
+ return oneShot;
+ }
+ }
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "No matching remote found to takeover (#%d)", info.getDebugId());
+ return null;
+ }
+
+ @Override
+ public boolean takeOverAnimation(
+ @NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction transaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull WindowAnimationState[] states) {
+ Transitions.TransitionHandler handler = getHandlerForTakeover(transition, info);
+ if (handler == null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "Take over request failed: no matching remote for (#%d)", info.getDebugId());
+ return false;
+ }
+ ((OneShotRemoteHandler) handler).setTransition(transition);
+ return handler.takeOverAnimation(transition, info, transaction, finishCallback, states);
+ }
+
@Override
@Nullable
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@@ -284,6 +341,34 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
}
}
+ void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+
+ pw.println(prefix + "Registered Remotes:");
+ if (mFilters.isEmpty()) {
+ pw.println(innerPrefix + "none");
+ } else {
+ for (Pair<TransitionFilter, RemoteTransition> entry : mFilters) {
+ dumpRemote(pw, innerPrefix, entry.second);
+ }
+ }
+
+ pw.println(prefix + "Registered Takeover Remotes:");
+ if (mTakeoverFilters.isEmpty()) {
+ pw.println(innerPrefix + "none");
+ } else {
+ for (Pair<TransitionFilter, RemoteTransition> entry : mTakeoverFilters) {
+ dumpRemote(pw, innerPrefix, entry.second);
+ }
+ }
+ }
+
+ private void dumpRemote(@NonNull PrintWriter pw, String prefix, RemoteTransition remote) {
+ pw.print(prefix);
+ pw.print(remote.getDebugName());
+ pw.println(" (" + Integer.toHexString(System.identityHashCode(remote)) + ")");
+ }
+
/** NOTE: binder deaths can alter the filter order */
private class RemoteDeathHandler implements IBinder.DeathRecipient {
private final IBinder mRemote;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 437a00e4a160..9b2922dff2f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -35,6 +35,7 @@ import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
+import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
@@ -43,9 +44,11 @@ import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SH
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
+import android.app.AppGlobals;
import android.app.IApplicationThread;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.IBinder;
@@ -62,6 +65,7 @@ import android.window.TransitionFilter;
import android.window.TransitionInfo;
import android.window.TransitionMetrics;
import android.window.TransitionRequestInfo;
+import android.window.WindowAnimationState;
import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
@@ -124,8 +128,7 @@ public class Transitions implements RemoteCallable<Transitions>,
static final String TAG = "ShellTransitions";
/** Set to {@code true} to enable shell transitions. */
- public static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
+ public static final boolean ENABLE_SHELL_TRANSITIONS = getShellTransitEnabled();
public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
&& SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
@@ -419,12 +422,24 @@ public class Transitions implements RemoteCallable<Transitions>,
mHandlers.set(0, handler);
}
- /** Register a remote transition to be used when `filter` matches an incoming transition */
+ /**
+ * Register a remote transition to be used for all operations except takeovers when `filter`
+ * matches an incoming transition.
+ */
public void registerRemote(@NonNull TransitionFilter filter,
@NonNull RemoteTransition remoteTransition) {
mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
}
+ /**
+ * Register a remote transition to be used for all operations except takeovers when `filter`
+ * matches an incoming transition.
+ */
+ public void registerRemoteForTakeover(@NonNull TransitionFilter filter,
+ @NonNull RemoteTransition remoteTransition) {
+ mRemoteTransitionHandler.addFilteredForTakeover(filter, remoteTransition);
+ }
+
/** Unregisters a remote transition and all associated filters */
public void unregisterRemote(@NonNull RemoteTransition remoteTransition) {
mRemoteTransitionHandler.removeFiltered(remoteTransition);
@@ -1187,6 +1202,29 @@ public class Transitions implements RemoteCallable<Transitions>,
}
/**
+ * Checks whether a handler exists capable of taking over the given transition, and returns it.
+ * Otherwise it returns null.
+ */
+ @Nullable
+ public TransitionHandler getHandlerForTakeover(
+ @NonNull IBinder transition, @NonNull TransitionInfo info) {
+ if (!returnAnimationFrameworkLibrary()) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "Trying to get a handler for takeover but the flag is disabled");
+ return null;
+ }
+
+ for (TransitionHandler handler : mHandlers) {
+ TransitionHandler candidate = handler.getHandlerForTakeover(transition, info);
+ if (candidate != null) {
+ return candidate;
+ }
+ }
+
+ return null;
+ }
+
+ /**
* Finish running animations (almost) immediately when a SLEEP transition comes in. We use this
* as both a way to reduce unnecessary work (animations not visible while screen off) and as a
* failsafe to unblock "stuck" animations (in particular remote animations).
@@ -1328,6 +1366,49 @@ public class Transitions implements RemoteCallable<Transitions>,
@NonNull TransitionFinishCallback finishCallback) { }
/**
+ * Checks whether this handler is capable of taking over a transition matching `info`.
+ * {@link TransitionHandler#takeOverAnimation(IBinder, TransitionInfo,
+ * SurfaceControl.Transaction, TransitionFinishCallback, WindowAnimationState[])} is
+ * guaranteed to succeed if called on the handler returned by this method.
+ *
+ * Note that the handler returned by this method can either be itself, or a different one
+ * selected by this handler to take care of the transition on its behalf.
+ *
+ * @param transition The transition that should be taken over.
+ * @param info Information about the transition to be taken over.
+ * @return A handler capable of taking over a matching transition, or null.
+ */
+ @Nullable
+ default TransitionHandler getHandlerForTakeover(
+ @NonNull IBinder transition, @NonNull TransitionInfo info) {
+ return null;
+ }
+
+ /**
+ * Attempt to take over a running transition. This must succeed if this handler was returned
+ * by {@link TransitionHandler#getHandlerForTakeover(IBinder, TransitionInfo)}.
+ *
+ * @param transition The transition that should be taken over.
+ * @param info Information about the what is changing in the transition.
+ * @param transaction Contains surface changes that resulted from the transition. Any
+ * additional changes should be added to this transaction and committed
+ * inside this method.
+ * @param finishCallback Call this at the end of the animation, if the take-over succeeds.
+ * Note that this will be called instead of the callback originally
+ * passed to startAnimation(), so the caller should make sure all
+ * necessary cleanup happens here. This MUST be called on main thread.
+ * @param states The animation states of the transition's window at the time this method was
+ * called.
+ * @return true if the transition was taken over, false if not.
+ */
+ default boolean takeOverAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction transaction,
+ @NonNull TransitionFinishCallback finishCallback,
+ @NonNull WindowAnimationState[] states) {
+ return false;
+ }
+
+ /**
* Potentially handles a startTransition request.
*
* @param transition The transition whose start is being requested.
@@ -1432,16 +1513,21 @@ public class Transitions implements RemoteCallable<Transitions>,
@Override
public void registerRemote(@NonNull TransitionFilter filter,
@NonNull RemoteTransition remoteTransition) {
- mMainExecutor.execute(() -> {
- mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
- });
+ mMainExecutor.execute(
+ () -> mRemoteTransitionHandler.addFiltered(filter, remoteTransition));
+ }
+
+ @Override
+ public void registerRemoteForTakeover(@NonNull TransitionFilter filter,
+ @NonNull RemoteTransition remoteTransition) {
+ mMainExecutor.execute(() -> mRemoteTransitionHandler.addFilteredForTakeover(
+ filter, remoteTransition));
}
@Override
public void unregisterRemote(@NonNull RemoteTransition remoteTransition) {
- mMainExecutor.execute(() -> {
- mRemoteTransitionHandler.removeFiltered(remoteTransition);
- });
+ mMainExecutor.execute(
+ () -> mRemoteTransitionHandler.removeFiltered(remoteTransition));
}
}
@@ -1470,17 +1556,23 @@ public class Transitions implements RemoteCallable<Transitions>,
public void registerRemote(@NonNull TransitionFilter filter,
@NonNull RemoteTransition remoteTransition) {
executeRemoteCallWithTaskPermission(mTransitions, "registerRemote",
- (transitions) -> {
- transitions.mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
- });
+ (transitions) -> transitions.mRemoteTransitionHandler.addFiltered(
+ filter, remoteTransition));
+ }
+
+ @Override
+ public void registerRemoteForTakeover(@NonNull TransitionFilter filter,
+ @NonNull RemoteTransition remoteTransition) {
+ executeRemoteCallWithTaskPermission(mTransitions, "registerRemoteForTakeover",
+ (transitions) -> transitions.mRemoteTransitionHandler.addFilteredForTakeover(
+ filter, remoteTransition));
}
@Override
public void unregisterRemote(@NonNull RemoteTransition remoteTransition) {
executeRemoteCallWithTaskPermission(mTransitions, "unregisterRemote",
- (transitions) -> {
- transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition);
- });
+ (transitions) ->
+ transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition));
}
@Override
@@ -1565,6 +1657,8 @@ public class Transitions implements RemoteCallable<Transitions>,
pw.println(" (" + Integer.toHexString(System.identityHashCode(handler)) + ")");
}
+ mRemoteTransitionHandler.dump(pw, prefix);
+
pw.println(prefix + "Observers:");
for (TransitionObserver observer : mObservers) {
pw.print(innerPrefix);
@@ -1617,4 +1711,16 @@ public class Transitions implements RemoteCallable<Transitions>,
}
}
}
+
+ private static boolean getShellTransitEnabled() {
+ try {
+ if (AppGlobals.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE, 0)) {
+ return SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
+ }
+ } catch (RemoteException re) {
+ Log.w(TAG, "Error getting system features");
+ }
+ return true;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
index fa331af267fa..1897560deed7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
@@ -16,7 +16,12 @@
package com.android.wm.shell.transition.tracing;
-import android.internal.perfetto.protos.PerfettoTrace;
+import static android.tracing.perfetto.DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT;
+
+import android.internal.perfetto.protos.ShellTransitionOuterClass.ShellHandlerMapping;
+import android.internal.perfetto.protos.ShellTransitionOuterClass.ShellHandlerMappings;
+import android.internal.perfetto.protos.ShellTransitionOuterClass.ShellTransition;
+import android.internal.perfetto.protos.TracePacketOuterClass.TracePacket;
import android.os.SystemClock;
import android.os.Trace;
import android.tracing.perfetto.DataSourceParams;
@@ -44,7 +49,8 @@ public class PerfettoTransitionTracer implements TransitionTracer {
public PerfettoTransitionTracer() {
Producer.init(InitArguments.DEFAULTS);
- mDataSource.register(DataSourceParams.DEFAULTS);
+ mDataSource.register(
+ new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
}
/**
@@ -72,11 +78,11 @@ public class PerfettoTransitionTracer implements TransitionTracer {
final int handlerId = getHandlerId(handler);
final ProtoOutputStream os = ctx.newTracePacket();
- final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
- os.write(PerfettoTrace.ShellTransition.ID, transitionId);
- os.write(PerfettoTrace.ShellTransition.DISPATCH_TIME_NS,
+ final long token = os.start(TracePacket.SHELL_TRANSITION);
+ os.write(ShellTransition.ID, transitionId);
+ os.write(ShellTransition.DISPATCH_TIME_NS,
SystemClock.elapsedRealtimeNanos());
- os.write(PerfettoTrace.ShellTransition.HANDLER, handlerId);
+ os.write(ShellTransition.HANDLER, handlerId);
os.end(token);
});
}
@@ -117,11 +123,11 @@ public class PerfettoTransitionTracer implements TransitionTracer {
private void doLogMergeRequested(int mergeRequestedTransitionId, int playingTransitionId) {
mDataSource.trace(ctx -> {
final ProtoOutputStream os = ctx.newTracePacket();
- final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
- os.write(PerfettoTrace.ShellTransition.ID, mergeRequestedTransitionId);
- os.write(PerfettoTrace.ShellTransition.MERGE_REQUEST_TIME_NS,
+ final long token = os.start(TracePacket.SHELL_TRANSITION);
+ os.write(ShellTransition.ID, mergeRequestedTransitionId);
+ os.write(ShellTransition.MERGE_REQUEST_TIME_NS,
SystemClock.elapsedRealtimeNanos());
- os.write(PerfettoTrace.ShellTransition.MERGE_TARGET, playingTransitionId);
+ os.write(ShellTransition.MERGE_TARGET, playingTransitionId);
os.end(token);
});
}
@@ -149,11 +155,11 @@ public class PerfettoTransitionTracer implements TransitionTracer {
private void doLogMerged(int mergeRequestedTransitionId, int playingTransitionId) {
mDataSource.trace(ctx -> {
final ProtoOutputStream os = ctx.newTracePacket();
- final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
- os.write(PerfettoTrace.ShellTransition.ID, mergeRequestedTransitionId);
- os.write(PerfettoTrace.ShellTransition.MERGE_TIME_NS,
+ final long token = os.start(TracePacket.SHELL_TRANSITION);
+ os.write(ShellTransition.ID, mergeRequestedTransitionId);
+ os.write(ShellTransition.MERGE_TIME_NS,
SystemClock.elapsedRealtimeNanos());
- os.write(PerfettoTrace.ShellTransition.MERGE_TARGET, playingTransitionId);
+ os.write(ShellTransition.MERGE_TARGET, playingTransitionId);
os.end(token);
});
}
@@ -180,9 +186,9 @@ public class PerfettoTransitionTracer implements TransitionTracer {
private void doLogAborted(int transitionId) {
mDataSource.trace(ctx -> {
final ProtoOutputStream os = ctx.newTracePacket();
- final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
- os.write(PerfettoTrace.ShellTransition.ID, transitionId);
- os.write(PerfettoTrace.ShellTransition.SHELL_ABORT_TIME_NS,
+ final long token = os.start(TracePacket.SHELL_TRANSITION);
+ os.write(ShellTransition.ID, transitionId);
+ os.write(ShellTransition.SHELL_ABORT_TIME_NS,
SystemClock.elapsedRealtimeNanos());
os.end(token);
});
@@ -196,14 +202,14 @@ public class PerfettoTransitionTracer implements TransitionTracer {
mDataSource.trace(ctx -> {
final ProtoOutputStream os = ctx.newTracePacket();
- final long mappingsToken = os.start(PerfettoTrace.TracePacket.SHELL_HANDLER_MAPPINGS);
+ final long mappingsToken = os.start(TracePacket.SHELL_HANDLER_MAPPINGS);
for (Map.Entry<String, Integer> entry : mHandlerMapping.entrySet()) {
final String handler = entry.getKey();
final int handlerId = entry.getValue();
- final long mappingEntryToken = os.start(PerfettoTrace.ShellHandlerMappings.MAPPING);
- os.write(PerfettoTrace.ShellHandlerMapping.ID, handlerId);
- os.write(PerfettoTrace.ShellHandlerMapping.NAME, handler);
+ final long mappingEntryToken = os.start(ShellHandlerMappings.MAPPING);
+ os.write(ShellHandlerMapping.ID, handlerId);
+ os.write(ShellHandlerMapping.NAME, handler);
os.end(mappingEntryToken);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 43fd32ba1750..6671391efdeb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -200,7 +200,6 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
mRelayoutParams.mSetTaskPositionAndCrop = setTaskCropAndPosition;
- mRelayoutParams.mAllowCaptionInputFallthrough = false;
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 777ab9c17218..922c55f49a33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -1037,7 +1037,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mSyncQueue,
mRootTaskDisplayAreaOrganizer);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
- windowDecoration.createResizeVeil();
final DragPositioningCallback dragPositioningCallback;
if (!DesktopModeStatus.isVeiledResizeEnabled()) {
@@ -1182,7 +1181,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final boolean inImmersiveMode = !source.isVisible();
// Calls WindowDecoration#relayout if decoration visibility needs to be updated
if (inImmersiveMode != mInImmersiveMode) {
- decor.relayout(decor.mTaskInfo);
+ if (Flags.enableDesktopWindowingImmersiveHandleHiding()) {
+ decor.relayout(decor.mTaskInfo);
+ }
mInImmersiveMode = inImmersiveMode;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index da1699cd6e33..cb6495c6af38 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -44,6 +44,7 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.os.Trace;
import android.util.Log;
import android.util.Size;
import android.view.Choreographer;
@@ -51,6 +52,7 @@ import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.WindowManager;
import android.widget.ImageButton;
import android.window.WindowContainerTransaction;
@@ -107,8 +109,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private MaximizeMenu mMaximizeMenu;
private ResizeVeil mResizeVeil;
-
- private Drawable mAppIconDrawable;
private Bitmap mAppIconBitmap;
private CharSequence mAppName;
@@ -160,7 +160,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mSyncQueue = syncQueue;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+ Trace.beginSection("DesktopModeWindowDecoration#loadAppInfo");
loadAppInfo();
+ Trace.endSection();
}
void setCaptionListeners(
@@ -206,6 +208,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+ Trace.beginSection("DesktopModeWindowDecoration#relayout");
if (isHandleMenuActive()) {
mHandleMenu.relayout(startT);
}
@@ -217,16 +220,22 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ Trace.beginSection("DesktopModeWindowDecoration#relayout-inner");
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
+ Trace.endSection();
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
+ Trace.beginSection("DesktopModeWindowDecoration#relayout-applyWCT");
mTaskOrganizer.applyTransaction(wct);
+ Trace.endSection();
if (mResult.mRootView == null) {
// This means something blocks the window decor from showing, e.g. the task is hidden.
// Nothing is set up in this case including the decoration surface.
+ Trace.endSection(); // DesktopModeWindowDecoration#relayout
return;
}
+
if (oldRootView != mResult.mRootView) {
if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_focused_window_decor) {
mWindowDecorViewHolder = new DesktopModeFocusedWindowDecorationViewHolder(
@@ -254,7 +263,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
throw new IllegalArgumentException("Unexpected layout resource id");
}
}
+ Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
mWindowDecorViewHolder.bindData(mTaskInfo);
+ Trace.endSection();
if (!mTaskInfo.isFocused) {
closeHandleMenu();
@@ -270,11 +281,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
updateExclusionRegion();
}
closeDragResizeListener();
+ Trace.endSection(); // DesktopModeWindowDecoration#relayout
return;
}
if (oldDecorationSurface != mDecorationContainerSurface || mDragResizeListener == null) {
closeDragResizeListener();
+ Trace.beginSection("DesktopModeWindowDecoration#relayout-DragResizeInputListener");
mDragResizeListener = new DragResizeInputListener(
mContext,
mHandler,
@@ -285,6 +298,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mSurfaceControlBuilderSupplier,
mSurfaceControlTransactionSupplier,
mDisplayController);
+ Trace.endSection();
}
final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
@@ -309,6 +323,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
}
}
+ Trace.endSection(); // DesktopModeWindowDecoration#relayout
}
@VisibleForTesting
@@ -326,11 +341,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);
if (captionLayoutId == R.layout.desktop_mode_app_controls_window_decor) {
- // If the app is requesting to customize the caption bar, allow input to fall through
- // to the windows below so that the app can respond to input events on their custom
- // content.
- relayoutParams.mAllowCaptionInputFallthrough =
- TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo);
+ if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
+ // If the app is requesting to customize the caption bar, allow input to fall
+ // through to the windows below so that the app can respond to input events on
+ // their custom content.
+ relayoutParams.mInputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+ }
// Report occluding elements as bounding rects to the insets system so that apps can
// draw in the empty space in the center:
// First, the "app chip" section of the caption bar (+ some extra margins).
@@ -345,6 +361,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
controlsElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_end;
controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END;
relayoutParams.mOccludingCaptionElements.add(controlsElement);
+ } else if (captionLayoutId == R.layout.desktop_mode_focused_window_decor) {
+ // The focused decor (fullscreen/split) does not need to handle input because input in
+ // the App Handle is handled by the InputMonitor in DesktopModeWindowDecorViewModel.
+ relayoutParams.mInputFeatures
+ |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
}
if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ taskInfo.isFocused)) {
relayoutParams.mShadowRadiusId = taskInfo.isFocused
@@ -444,12 +465,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
PackageManager pm = mContext.getApplicationContext().getPackageManager();
final IconProvider provider = new IconProvider(mContext);
- mAppIconDrawable = provider.getIcon(activityInfo);
+ final Drawable appIconDrawable = provider.getIcon(activityInfo);
final Resources resources = mContext.getResources();
final BaseIconFactory factory = new BaseIconFactory(mContext,
resources.getDisplayMetrics().densityDpi,
resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius));
- mAppIconBitmap = factory.createScaledBitmap(mAppIconDrawable, MODE_DEFAULT);
+ mAppIconBitmap = factory.createScaledBitmap(appIconDrawable, MODE_DEFAULT);
final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
mAppName = pm.getApplicationLabel(applicationInfo);
}
@@ -466,8 +487,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* Create the resize veil for this task. Note the veil's visibility is View.GONE by default
* until a resize event calls showResizeVeil below.
*/
- void createResizeVeil() {
- mResizeVeil = new ResizeVeil(mContext, mDisplayController, mAppIconDrawable, mTaskInfo,
+ private void createResizeVeilIfNeeded() {
+ if (mResizeVeil != null) return;
+ mResizeVeil = new ResizeVeil(mContext, mDisplayController, mAppIconBitmap, mTaskInfo,
mTaskSurface, mSurfaceControlTransactionSupplier);
}
@@ -475,6 +497,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* Show the resize veil.
*/
public void showResizeVeil(Rect taskBounds) {
+ createResizeVeilIfNeeded();
mResizeVeil.showVeil(mTaskSurface, taskBounds);
}
@@ -482,6 +505,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* Show the resize veil.
*/
public void showResizeVeil(SurfaceControl.Transaction tx, Rect taskBounds) {
+ createResizeVeilIfNeeded();
mResizeVeil.showVeil(tx, mTaskSurface, taskBounds, false /* fadeIn */);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
index 2c4092ac6d2c..93e2a21c6b02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
@@ -24,11 +24,12 @@ import android.annotation.NonNull;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
+import android.os.Trace;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
@@ -64,7 +65,7 @@ public class ResizeVeil {
private final SurfaceControlBuilderFactory mSurfaceControlBuilderFactory;
private final WindowDecoration.SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
private final SurfaceSession mSurfaceSession = new SurfaceSession();
- private final Drawable mAppIcon;
+ private final Bitmap mAppIcon;
private ImageView mIconView;
private int mIconSize;
private SurfaceControl mParentSurface;
@@ -97,7 +98,7 @@ public class ResizeVeil {
public ResizeVeil(Context context,
@NonNull DisplayController displayController,
- Drawable appIcon, RunningTaskInfo taskInfo,
+ Bitmap appIcon, RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
this(context,
@@ -112,7 +113,7 @@ public class ResizeVeil {
public ResizeVeil(Context context,
@NonNull DisplayController displayController,
- Drawable appIcon, RunningTaskInfo taskInfo,
+ Bitmap appIcon, RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
SurfaceControlBuilderFactory surfaceControlBuilderFactory,
@@ -135,6 +136,7 @@ public class ResizeVeil {
// Display may not be available yet, skip this until then.
return;
}
+ Trace.beginSection("ResizeVeil#setupResizeVeil");
mVeilSurface = mSurfaceControlBuilderFactory
.create("Resize veil of Task=" + mTaskInfo.taskId)
.setContainerLayer()
@@ -162,7 +164,7 @@ public class ResizeVeil {
final View root = LayoutInflater.from(mContext)
.inflate(R.layout.desktop_mode_resize_veil, null /* root */);
mIconView = root.findViewById(R.id.veil_application_icon);
- mIconView.setImageDrawable(mAppIcon);
+ mIconView.setImageBitmap(mAppIcon);
final WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(
@@ -179,6 +181,7 @@ public class ResizeVeil {
mViewHost = mSurfaceControlViewHostFactory.create(mContext, mDisplay, wwm, "ResizeVeil");
mViewHost.setView(root, lp);
+ Trace.endSection();
}
private boolean obtainDisplayOrRegisterListener() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 36da1ace8408..54656ffa1e93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -33,6 +33,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
+import android.os.Trace;
import android.view.Display;
import android.view.InsetsSource;
import android.view.InsetsState;
@@ -311,7 +312,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
boundingRects = null;
} else {
// The customizable region can at most be equal to the caption bar.
- if (params.mAllowCaptionInputFallthrough) {
+ if (params.hasInputFeatureSpy()) {
outResult.mCustomizableCaptionRegion.set(mCaptionInsetsRect);
}
boundingRects = new Rect[numOfElements];
@@ -324,7 +325,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
calculateBoundingRect(element, elementWidthPx, mCaptionInsetsRect);
// Subtract the regions used by the caption elements, the rest is
// customizable.
- if (params.mAllowCaptionInputFallthrough) {
+ if (params.hasInputFeatureSpy()) {
outResult.mCustomizableCaptionRegion.op(boundingRects[i],
Region.Op.DIFFERENCE);
}
@@ -378,6 +379,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
startT.unsetColor(mTaskSurface);
}
+ Trace.beginSection("CaptionViewHostLayout");
if (mCaptionWindowManager == null) {
// Put caption under a container surface because ViewRootImpl sets the destination frame
// of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
@@ -394,11 +396,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
lp.setTrustedOverlay();
- if (params.mAllowCaptionInputFallthrough) {
- lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY;
- } else {
- lp.inputFeatures &= ~WindowManager.LayoutParams.INPUT_FEATURE_SPY;
- }
+ lp.inputFeatures = params.mInputFeatures;
if (mViewHost == null) {
mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
mCaptionWindowManager);
@@ -412,6 +410,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
mViewHost.relayout(lp);
}
+ Trace.endSection(); // CaptionViewHostLayout
}
private Rect calculateBoundingRect(@NonNull OccludingCaptionElement element,
@@ -605,7 +604,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
int mCaptionHeightId;
int mCaptionWidthId;
final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>();
- boolean mAllowCaptionInputFallthrough;
+ int mInputFeatures;
int mShadowRadiusId;
int mCornerRadius;
@@ -620,7 +619,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mCaptionHeightId = Resources.ID_NULL;
mCaptionWidthId = Resources.ID_NULL;
mOccludingCaptionElements.clear();
- mAllowCaptionInputFallthrough = false;
+ mInputFeatures = 0;
mShadowRadiusId = Resources.ID_NULL;
mCornerRadius = 0;
@@ -630,6 +629,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mWindowDecorConfig = null;
}
+ boolean hasInputFeatureSpy() {
+ return (mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_SPY) != 0;
+ }
+
/**
* Describes elements within the caption bar that could occlude app content, and should be
* sent as bounding rectangles to the insets system.
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index d718e157afdb..0f24bb549158 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -12,3 +12,4 @@ jorgegil@google.com
nmusgrave@google.com
pbdr@google.com
tkachenkoi@google.com
+mpodolian@google.com
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/OWNERS b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/OWNERS
new file mode 100644
index 000000000000..73a5a23909c5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/OWNERS
@@ -0,0 +1,5 @@
+# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > WM Shell > Freeform
+# Bug component: 929241
+
+uysalorhan@google.com
+pragyabajoria@google.com \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitLandscape.kt
new file mode 100644
index 000000000000..5563bb9fa934
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitLandscape.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 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.flicker.service.desktopmode.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_APP
+import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_LAST_APP
+import com.android.wm.shell.flicker.service.desktopmode.scenarios.CloseAllAppsWithAppHeaderExit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class CloseAllAppWithAppHeaderExitLandscape : CloseAllAppsWithAppHeaderExit(Rotation.ROTATION_90) {
+ @ExpectedScenarios(["CLOSE_APP", "CLOSE_LAST_APP"])
+ @Test
+ override fun closeAllAppsInDesktop() = super.closeAllAppsInDesktop()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig()
+ .use(FlickerServiceConfig.DEFAULT)
+ .use(CLOSE_APP)
+ .use(CLOSE_LAST_APP)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitPortrait.kt
new file mode 100644
index 000000000000..3d16d2219c78
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitPortrait.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 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.flicker.service.desktopmode.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_APP
+import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_LAST_APP
+import com.android.wm.shell.flicker.service.desktopmode.scenarios.CloseAllAppsWithAppHeaderExit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class CloseAllAppWithAppHeaderExitPortrait : CloseAllAppsWithAppHeaderExit(Rotation.ROTATION_0) {
+ @ExpectedScenarios(["CLOSE_APP", "CLOSE_LAST_APP"])
+ @Test
+ override fun closeAllAppsInDesktop() = super.closeAllAppsInDesktop()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig()
+ .use(FlickerServiceConfig.DEFAULT)
+ .use(CLOSE_APP)
+ .use(CLOSE_LAST_APP)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
new file mode 100644
index 000000000000..75dfeba3e662
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 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.flicker.service.desktopmode.flicker
+
+import android.tools.flicker.AssertionInvocationGroup
+import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd
+import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways
+import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart
+import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd
+import android.tools.flicker.assertors.assertions.AppWindowOnTopAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowOnTopAtStart
+import android.tools.flicker.assertors.assertions.LauncherWindowMovesToTop
+import android.tools.flicker.config.AssertionTemplates
+import android.tools.flicker.config.FlickerConfigEntry
+import android.tools.flicker.config.ScenarioId
+import android.tools.flicker.config.desktopmode.Components
+import android.tools.flicker.extractors.ITransitionMatcher
+import android.tools.flicker.extractors.ShellTransitionScenarioExtractor
+import android.tools.traces.wm.Transition
+import android.tools.traces.wm.TransitionType
+
+class DesktopModeFlickerScenarios {
+ companion object {
+ val END_DRAG_TO_DESKTOP =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("END_DRAG_TO_DESKTOP"),
+ extractor =
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection<Transition>
+ ): Collection<Transition> {
+ return transitions.filter {
+ it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP
+ }
+ }
+ }
+ ),
+ assertions =
+ AssertionTemplates.COMMON_ASSERTIONS +
+ listOf(
+ AppLayerIsVisibleAlways(Components.DESKTOP_MODE_APP),
+ AppWindowOnTopAtEnd(Components.DESKTOP_MODE_APP),
+ AppWindowHasDesktopModeInitialBoundsAtTheEnd(
+ Components.DESKTOP_MODE_APP
+ )
+ )
+ .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
+ val CLOSE_APP =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("CLOSE_APP"),
+ extractor =
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection<Transition>
+ ): Collection<Transition> {
+ return transitions.filter { it.type == TransitionType.CLOSE }
+ }
+ }
+ ),
+ assertions =
+ AssertionTemplates.COMMON_ASSERTIONS +
+ listOf(
+ AppWindowOnTopAtStart(Components.DESKTOP_MODE_APP),
+ AppLayerIsVisibleAtStart(Components.DESKTOP_MODE_APP),
+ AppLayerIsInvisibleAtEnd(Components.DESKTOP_MODE_APP),
+ )
+ .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
+ val CLOSE_LAST_APP =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("CLOSE_LAST_APP"),
+ extractor =
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection<Transition>
+ ): Collection<Transition> {
+ val lastTransition =
+ transitions.findLast { it.type == TransitionType.CLOSE }
+ return if (lastTransition != null) listOf(lastTransition)
+ else emptyList()
+ }
+ }
+ ),
+ assertions =
+ AssertionTemplates.COMMON_ASSERTIONS +
+ listOf(
+ AppWindowOnTopAtStart(Components.DESKTOP_MODE_APP),
+ AppLayerIsVisibleAtStart(Components.DESKTOP_MODE_APP),
+ AppLayerIsInvisibleAtEnd(Components.DESKTOP_MODE_APP),
+ LauncherWindowMovesToTop()
+ )
+ .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragLandscape.kt
new file mode 100644
index 000000000000..9dfafe958b0b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 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.flicker.service.desktopmode.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.END_DRAG_TO_DESKTOP
+import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithDrag
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterDesktopWithDragLandscape : EnterDesktopWithDrag(Rotation.ROTATION_90) {
+ @ExpectedScenarios(["END_DRAG_TO_DESKTOP"])
+ @Test
+ override fun enterDesktopWithDrag() = super.enterDesktopWithDrag()
+
+ companion object {
+
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(END_DRAG_TO_DESKTOP)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragPortrait.kt
new file mode 100644
index 000000000000..1c7d6237eb8a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragPortrait.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 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.flicker.service.desktopmode.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.END_DRAG_TO_DESKTOP
+import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithDrag
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterDesktopWithDragPortrait : EnterDesktopWithDrag(Rotation.ROTATION_0) {
+ @ExpectedScenarios(["END_DRAG_TO_DESKTOP"])
+ @Test
+ override fun enterDesktopWithDrag() = super.enterDesktopWithDrag()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(END_DRAG_TO_DESKTOP)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/CloseAllAppsWithAppHeaderExit.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/CloseAllAppsWithAppHeaderExit.kt
new file mode 100644
index 000000000000..0c2b5015840d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/CloseAllAppsWithAppHeaderExit.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 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.flicker.service.desktopmode.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.flicker.service.common.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class CloseAllAppsWithAppHeaderExit
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
+ private val nonResizeableApp = DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
+
+
+
+ @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode())
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ testApp.enterDesktopWithDrag(wmHelper, device)
+ mailApp.launchViaIntent(wmHelper)
+ nonResizeableApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun closeAllAppsInDesktop() {
+ nonResizeableApp.closeDesktopApp(wmHelper, device)
+ mailApp.closeDesktopApp(wmHelper, device)
+ testApp.closeDesktopApp(wmHelper, device)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithDrag.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithDrag.kt
new file mode 100644
index 000000000000..9e9998ef7c2a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithDrag.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 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.flicker.service.desktopmode.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.flicker.service.common.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+
+@Ignore("Base Test Class")
+abstract class EnterDesktopWithDrag
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+
+ @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode())
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ }
+
+ @Test
+ open fun enterDesktopWithDrag() {
+ testApp.enterDesktopWithDrag(wmHelper, device)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 9c1a88e1caa0..82c070cbf1c3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -16,10 +16,10 @@
package com.android.wm.shell;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -435,7 +435,8 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
public void testOnCameraCompatActivityChanged() {
final RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN);
taskInfo1.displayId = DEFAULT_DISPLAY;
- taskInfo1.appCompatTaskInfo.cameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
+ taskInfo1.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
+ CAMERA_COMPAT_CONTROL_HIDDEN;
final TrackingTaskListener taskListener = new TrackingTaskListener();
mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN);
mOrganizer.onTaskAppeared(taskInfo1, null);
@@ -449,7 +450,7 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
final RunningTaskInfo taskInfo2 =
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
taskInfo2.displayId = taskInfo1.displayId;
- taskInfo2.appCompatTaskInfo.cameraCompatControlState =
+ taskInfo2.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
taskInfo2.isVisible = true;
mOrganizer.onTaskInfoChanged(taskInfo2);
@@ -461,7 +462,7 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
final RunningTaskInfo taskInfo3 =
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
taskInfo3.displayId = taskInfo1.displayId;
- taskInfo3.appCompatTaskInfo.cameraCompatControlState =
+ taskInfo3.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
taskInfo3.isVisible = true;
mOrganizer.onTaskInfoChanged(taskInfo3);
@@ -474,7 +475,7 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
taskInfo4.displayId = taskInfo1.displayId;
taskInfo4.appCompatTaskInfo.topActivityInSizeCompat = true;
- taskInfo4.appCompatTaskInfo.cameraCompatControlState =
+ taskInfo4.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
taskInfo4.isVisible = true;
mOrganizer.onTaskInfoChanged(taskInfo4);
@@ -485,7 +486,7 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
final RunningTaskInfo taskInfo5 =
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
taskInfo5.displayId = taskInfo1.displayId;
- taskInfo5.appCompatTaskInfo.cameraCompatControlState =
+ taskInfo5.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
CAMERA_COMPAT_CONTROL_DISMISSED;
taskInfo5.isVisible = true;
mOrganizer.onTaskInfoChanged(taskInfo5);
@@ -496,7 +497,7 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
final RunningTaskInfo taskInfo6 =
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
taskInfo6.displayId = taskInfo1.displayId;
- taskInfo6.appCompatTaskInfo.cameraCompatControlState =
+ taskInfo6.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
taskInfo6.isVisible = false;
mOrganizer.onTaskInfoChanged(taskInfo6);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 65169e36a225..f99b4b2beef0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -354,6 +354,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
// Verify that we prevent any interaction with the animator callback in case a new gesture
// starts while the current back animation has not ended, instead the gesture is queued
triggerBackGesture();
+ verify(mAnimatorCallback).setTriggerBack(eq(true));
verifyNoMoreInteractions(mAnimatorCallback);
// Finish previous back navigation.
@@ -394,6 +395,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
// starts while the current back animation has not ended, instead the gesture is queued
triggerBackGesture();
releaseBackGesture();
+ verify(mAnimatorCallback).setTriggerBack(eq(true));
verifyNoMoreInteractions(mAnimatorCallback);
// Finish previous back navigation.
@@ -532,7 +534,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
}
@Test
- public void callbackShouldDeliverProgress() throws RemoteException {
+ public void appCallback_receivesStartAndInvoke() throws RemoteException {
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
final int type = BackNavigationInfo.TYPE_CALLBACK;
@@ -551,8 +553,9 @@ public class BackAnimationControllerTest extends ShellTestCase {
assertTrue("TriggerBack should have been true", result.mTriggerBack);
verify(mAppCallback, times(1)).onBackStarted(any());
- verify(mAppCallback, times(1)).onBackProgressed(any());
verify(mAppCallback, times(1)).onBackInvoked();
+ // Progress events should be generated from the app process.
+ verify(mAppCallback, never()).onBackProgressed(any());
verify(mAnimatorCallback, never()).onBackStarted(any());
verify(mAnimatorCallback, never()).onBackProgressed(any());
@@ -639,7 +642,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
*/
private void doStartEvents(int startX, int moveX) {
doMotionEvent(MotionEvent.ACTION_DOWN, startX);
- mController.onPilferPointers();
+ mController.onThresholdCrossed();
doMotionEvent(MotionEvent.ACTION_MOVE, moveX);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
index cebbbd890f05..158d640dca30 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
@@ -96,7 +96,7 @@ public class CustomizeActivityAnimationTest extends ShellTestCase {
.loadAnimation(any(), eq(true));
mCustomizeActivityAnimation.prepareNextAnimation(
- new BackNavigationInfo.CustomAnimationInfo("TestPackage"));
+ new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
final RemoteAnimationTarget close = createAnimationTarget(false);
final RemoteAnimationTarget open = createAnimationTarget(true);
// start animation with remote animation targets
@@ -129,7 +129,7 @@ public class CustomizeActivityAnimationTest extends ShellTestCase {
.loadAnimation(any(), eq(true));
mCustomizeActivityAnimation.prepareNextAnimation(
- new BackNavigationInfo.CustomAnimationInfo("TestPackage"));
+ new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
final RemoteAnimationTarget close = createAnimationTarget(false);
final RemoteAnimationTarget open = createAnimationTarget(true);
// start animation with remote animation targets
@@ -155,7 +155,7 @@ public class CustomizeActivityAnimationTest extends ShellTestCase {
@Test
public void receiveFinishWithoutAnimationAfterInvoke() throws InterruptedException {
mCustomizeActivityAnimation.prepareNextAnimation(
- new BackNavigationInfo.CustomAnimationInfo("TestPackage"));
+ new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
// start animation without any remote animation targets
final CountDownLatch finishCalled = new CountDownLatch(1);
final Runnable finishCallback = finishCalled::countDown;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
deleted file mode 100644
index 6dbb1e2b8d92..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2022 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.back
-
-import android.util.MathUtils
-import android.window.BackEvent
-import org.junit.Assert.assertEquals
-import org.junit.Test
-
-class TouchTrackerTest {
- private fun linearTouchTracker(): TouchTracker = TouchTracker().apply {
- setProgressThresholds(MAX_DISTANCE, MAX_DISTANCE, NON_LINEAR_FACTOR)
- }
-
- private fun nonLinearTouchTracker(): TouchTracker = TouchTracker().apply {
- setProgressThresholds(LINEAR_DISTANCE, MAX_DISTANCE, NON_LINEAR_FACTOR)
- }
-
- private fun TouchTracker.assertProgress(expected: Float) {
- val actualProgress = createProgressEvent().progress
- assertEquals(expected, actualProgress, /* delta = */ 0f)
- }
-
- @Test
- fun generatesProgress_onStart() {
- val linearTracker = linearTouchTracker()
- linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
- val event = linearTracker.createStartEvent(null)
- assertEquals(0f, event.progress, 0f)
- }
-
- @Test
- fun generatesProgress_leftEdge() {
- val linearTracker = linearTouchTracker()
- linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
- var touchX = 10f
- val velocityX = 0f
- val velocityY = 0f
-
- // Pre-commit
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
-
- // Post-commit
- touchX += 100f
- linearTracker.setTriggerBack(true)
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
-
- // Cancel
- touchX -= 10f
- linearTracker.setTriggerBack(false)
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress(0f)
-
- // Cancel more
- touchX -= 10f
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress(0f)
-
- // Restarted, but pre-commit
- val restartX = touchX
- touchX += 10f
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((touchX - restartX) / MAX_DISTANCE)
-
- // continue restart within pre-commit
- touchX += 10f
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((touchX - restartX) / MAX_DISTANCE)
-
- // Restarted, post-commit
- touchX += 10f
- linearTracker.setTriggerBack(true)
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
- }
-
- @Test
- fun generatesProgress_rightEdge() {
- val linearTracker = linearTouchTracker()
- linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT)
- var touchX = INITIAL_X_RIGHT_EDGE - 10 // Fake right edge
- val velocityX = 0f
- val velocityY = 0f
- val target = MAX_DISTANCE
-
- // Pre-commit
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target)
-
- // Post-commit
- touchX -= 100f
- linearTracker.setTriggerBack(true)
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target)
-
- // Cancel
- touchX += 10f
- linearTracker.setTriggerBack(false)
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress(0f)
-
- // Cancel more
- touchX += 10f
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress(0f)
-
- // Restarted, but pre-commit
- val restartX = touchX
- touchX -= 10f
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((restartX - touchX) / target)
-
- // continue restart within pre-commit
- touchX -= 10f
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((restartX - touchX) / target)
-
- // Restarted, post-commit
- touchX -= 10f
- linearTracker.setTriggerBack(true)
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target)
- }
-
- @Test
- fun generatesNonLinearProgress_leftEdge() {
- val nonLinearTracker = nonLinearTouchTracker()
- nonLinearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
- var touchX = 10f
- val velocityX = 0f
- val velocityY = 0f
- val linearTarget = LINEAR_DISTANCE + (MAX_DISTANCE - LINEAR_DISTANCE) * NON_LINEAR_FACTOR
-
- // Pre-commit: linear progress
- nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
- nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget)
-
- // Post-commit: still linear progress
- touchX += 100f
- nonLinearTracker.setTriggerBack(true)
- nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
- nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget)
-
- // still linear progress
- touchX = INITIAL_X_LEFT_EDGE + LINEAR_DISTANCE
- nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
- nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget)
-
- // non linear progress
- touchX += 10
- nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
- val nonLinearTouch = (touchX - INITIAL_X_LEFT_EDGE) - LINEAR_DISTANCE
- val nonLinearProgress = nonLinearTouch / NON_LINEAR_DISTANCE
- val nonLinearTarget = MathUtils.lerp(linearTarget, MAX_DISTANCE, nonLinearProgress)
- nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / nonLinearTarget)
- }
-
- @Test
- fun restartingGesture_resetsInitialTouchX_leftEdge() {
- val linearTracker = linearTouchTracker()
- linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
- var touchX = 100f
- val velocityX = 0f
- val velocityY = 0f
-
- // assert that progress is increased when increasing touchX
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
-
- // assert that progress is reset to 0 when start location is updated
- linearTracker.updateStartLocation()
- linearTracker.assertProgress(0f)
-
- // assert that progress remains 0 when touchX is decreased
- touchX -= 50
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress(0f)
-
- // assert that progress uses new minimal touchX for progress calculation
- val newInitialTouchX = touchX
- touchX += 100
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE)
-
- // assert the same for triggerBack==true
- linearTracker.triggerBack = true
- linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE)
- }
-
- @Test
- fun restartingGesture_resetsInitialTouchX_rightEdge() {
- val linearTracker = linearTouchTracker()
- linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT)
-
- var touchX = INITIAL_X_RIGHT_EDGE - 100f
- val velocityX = 0f
- val velocityY = 0f
-
- // assert that progress is increased when decreasing touchX
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / MAX_DISTANCE)
-
- // assert that progress is reset to 0 when start location is updated
- linearTracker.updateStartLocation()
- linearTracker.assertProgress(0f)
-
- // assert that progress remains 0 when touchX is increased
- touchX += 50
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress(0f)
-
- // assert that progress uses new maximal touchX for progress calculation
- val newInitialTouchX = touchX
- touchX -= 100
- linearTracker.update(touchX, 0f, velocityX, velocityY)
- linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE)
-
- // assert the same for triggerBack==true
- linearTracker.triggerBack = true
- linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE)
- }
-
- companion object {
- private const val MAX_DISTANCE = 500f
- private const val LINEAR_DISTANCE = 400f
- private const val NON_LINEAR_DISTANCE = MAX_DISTANCE - LINEAR_DISTANCE
- private const val NON_LINEAR_FACTOR = 0.2f
- private const val INITIAL_X_LEFT_EDGE = 5f
- private const val INITIAL_X_RIGHT_EDGE = MAX_DISTANCE - INITIAL_X_LEFT_EDGE
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index fef81af8946b..afae653f0682 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -16,8 +16,8 @@
package com.android.wm.shell.compatui;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.view.WindowInsets.Type.navigationBars;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -34,7 +34,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.AppCompatTaskInfo.CameraCompatControlState;
+import android.app.CameraCompatTaskInfo.CameraCompatControlState;
import android.app.TaskInfo;
import android.content.Context;
import android.content.res.Configuration;
@@ -689,7 +689,8 @@ public class CompatUIControllerTest extends ShellTestCase {
taskInfo.taskId = taskId;
taskInfo.displayId = displayId;
taskInfo.appCompatTaskInfo.topActivityInSizeCompat = hasSizeCompat;
- taskInfo.appCompatTaskInfo.cameraCompatControlState = cameraCompatControlState;
+ taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
+ cameraCompatControlState;
taskInfo.isVisible = isVisible;
taskInfo.isFocused = isFocused;
taskInfo.isTopActivityTransparent = isTopActivityTransparent;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index dd358e757fde..cd3e8cb0e8e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -16,10 +16,10 @@
package com.android.wm.shell.compatui;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -28,7 +28,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
-import android.app.AppCompatTaskInfo.CameraCompatControlState;
+import android.app.CameraCompatTaskInfo.CameraCompatControlState;
import android.app.TaskInfo;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -222,7 +222,8 @@ public class CompatUILayoutTest extends ShellTestCase {
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = TASK_ID;
taskInfo.appCompatTaskInfo.topActivityInSizeCompat = hasSizeCompat;
- taskInfo.appCompatTaskInfo.cameraCompatControlState = cameraCompatControlState;
+ taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
+ cameraCompatControlState;
taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = 1000;
taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1000;
taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 2000, 2000));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index 4f261cd79d39..5209d0e4bdd0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -16,10 +16,10 @@
package com.android.wm.shell.compatui;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.WindowInsets.Type.navigationBars;
@@ -37,7 +37,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
-import android.app.AppCompatTaskInfo;
+import android.app.CameraCompatTaskInfo;
import android.app.TaskInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -521,11 +521,12 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
}
private static TaskInfo createTaskInfo(boolean hasSizeCompat,
- @AppCompatTaskInfo.CameraCompatControlState int cameraCompatControlState) {
+ @CameraCompatTaskInfo.CameraCompatControlState int cameraCompatControlState) {
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = TASK_ID;
taskInfo.appCompatTaskInfo.topActivityInSizeCompat = hasSizeCompat;
- taskInfo.appCompatTaskInfo.cameraCompatControlState = cameraCompatControlState;
+ taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
+ cameraCompatControlState;
taskInfo.configuration.uiMode &= ~Configuration.UI_MODE_TYPE_DESK;
// Letterboxed activity that takes half the screen should show size compat restart button
taskInfo.configuration.windowConfiguration.setBounds(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
index 38d6ea1839c4..02316125bcc3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.compatui;
-import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -25,7 +25,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
-import android.app.AppCompatTaskInfo.CameraCompatControlState;
+import android.app.CameraCompatTaskInfo.CameraCompatControlState;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.testing.AndroidTestingRunner;
@@ -148,7 +148,8 @@ public class UserAspectRatioSettingsLayoutTest extends ShellTestCase {
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = TASK_ID;
taskInfo.appCompatTaskInfo.topActivityInSizeCompat = hasSizeCompat;
- taskInfo.appCompatTaskInfo.cameraCompatControlState = cameraCompatControlState;
+ taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
+ cameraCompatControlState;
taskInfo.realActivity = new ComponentName("com.mypackage.test", "TestActivity");
return taskInfo;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index 81ba4b37d13b..94e168ed70ed 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -292,6 +292,24 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {
}
@Test
+ public void testUserFullscreenOverrideEnabled_buttonAlwaysShown() {
+ TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
+
+ final Rect stableBounds = mWindowManager.getTaskStableBounds();
+
+ // Letterboxed activity that has user fullscreen override should always show button,
+ // layout should be inflated
+ taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = stableBounds.height();
+ taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = stableBounds.width();
+ taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled = true;
+
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+
+ verify(mWindowManager).inflateLayout();
+ }
+
+ @Test
public void testUpdateDisplayLayout() {
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 1000;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index b2b54acf4585..dca7be12fffc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -483,6 +483,102 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
assertThat(repo.removeBoundsBeforeMaximize(taskId)).isNull()
}
+ @Test
+ fun minimizeTaskNotCalled_noTasksMinimized() {
+ assertThat(repo.isMinimizedTask(taskId = 0)).isFalse()
+ assertThat(repo.isMinimizedTask(taskId = 1)).isFalse()
+ }
+
+ @Test
+ fun minimizeTask_onlyThatTaskIsMinimized() {
+ repo.minimizeTask(displayId = 0, taskId = 0)
+
+ assertThat(repo.isMinimizedTask(taskId = 0)).isTrue()
+ assertThat(repo.isMinimizedTask(taskId = 1)).isFalse()
+ assertThat(repo.isMinimizedTask(taskId = 2)).isFalse()
+ }
+
+ @Test
+ fun unminimizeTask_taskNoLongerMinimized() {
+ repo.minimizeTask(displayId = 0, taskId = 0)
+ repo.unminimizeTask(displayId = 0, taskId = 0)
+
+ assertThat(repo.isMinimizedTask(taskId = 0)).isFalse()
+ assertThat(repo.isMinimizedTask(taskId = 1)).isFalse()
+ assertThat(repo.isMinimizedTask(taskId = 2)).isFalse()
+ }
+
+ @Test
+ fun unminimizeTask_nonExistentTask_doesntCrash() {
+ repo.unminimizeTask(displayId = 0, taskId = 0)
+
+ assertThat(repo.isMinimizedTask(taskId = 0)).isFalse()
+ assertThat(repo.isMinimizedTask(taskId = 1)).isFalse()
+ assertThat(repo.isMinimizedTask(taskId = 2)).isFalse()
+ }
+
+
+ @Test
+ fun updateVisibleFreeformTasks_toVisible_taskIsUnminimized() {
+ repo.minimizeTask(displayId = 10, taskId = 2)
+
+ repo.updateVisibleFreeformTasks(displayId = 10, taskId = 2, visible = true)
+
+ assertThat(repo.isMinimizedTask(taskId = 2)).isFalse()
+ }
+
+ @Test
+ fun isDesktopModeShowing_noActiveTasks_returnsFalse() {
+ assertThat(repo.isDesktopModeShowing(displayId = 0)).isFalse()
+ }
+
+ @Test
+ fun isDesktopModeShowing_noTasksVisible_returnsFalse() {
+ repo.addActiveTask(displayId = 0, taskId = 1)
+ repo.addActiveTask(displayId = 0, taskId = 2)
+
+ assertThat(repo.isDesktopModeShowing(displayId = 0)).isFalse()
+ }
+
+ @Test
+ fun isDesktopModeShowing_tasksActiveAndVisible_returnsTrue() {
+ repo.addActiveTask(displayId = 0, taskId = 1)
+ repo.addActiveTask(displayId = 0, taskId = 2)
+ repo.updateVisibleFreeformTasks(displayId = 0, taskId = 1, visible = true)
+
+ assertThat(repo.isDesktopModeShowing(displayId = 0)).isTrue()
+ }
+
+ @Test
+ fun getActiveNonMinimizedTasksOrderedFrontToBack_returnsFreeformTasksInCorrectOrder() {
+ repo.addActiveTask(displayId = 0, taskId = 1)
+ repo.addActiveTask(displayId = 0, taskId = 2)
+ repo.addActiveTask(displayId = 0, taskId = 3)
+ // The front-most task will be the one added last through addOrMoveFreeformTaskToTop
+ repo.addOrMoveFreeformTaskToTop(taskId = 3)
+ repo.addOrMoveFreeformTaskToTop(taskId = 2)
+ repo.addOrMoveFreeformTaskToTop(taskId = 1)
+
+ assertThat(repo.getActiveNonMinimizedTasksOrderedFrontToBack(displayId = 0)).isEqualTo(
+ listOf(1, 2, 3))
+ }
+
+ @Test
+ fun getActiveNonMinimizedTasksOrderedFrontToBack_minimizedTaskNotIncluded() {
+ repo.addActiveTask(displayId = 0, taskId = 1)
+ repo.addActiveTask(displayId = 0, taskId = 2)
+ repo.addActiveTask(displayId = 0, taskId = 3)
+ // The front-most task will be the one added last through addOrMoveFreeformTaskToTop
+ repo.addOrMoveFreeformTaskToTop(taskId = 3)
+ repo.addOrMoveFreeformTaskToTop(taskId = 2)
+ repo.addOrMoveFreeformTaskToTop(taskId = 1)
+ repo.minimizeTask(displayId = 0, taskId = 2)
+
+ assertThat(repo.getActiveNonMinimizedTasksOrderedFrontToBack(displayId = 0)).isEqualTo(
+ listOf(1, 3))
+ }
+
+
class TestListener : DesktopModeTaskRepository.ActiveTasksListener {
var activeChangesOnDefaultDisplay = 0
var activeChangesOnSecondaryDisplay = 0
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 64f604119a8b..ad4b720facd7 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
@@ -105,6 +105,7 @@ import org.mockito.Mockito.verify
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.capture
import org.mockito.quality.Strictness
+import java.util.Optional
import org.mockito.Mockito.`when` as whenever
/**
@@ -145,6 +146,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
private lateinit var controller: DesktopTasksController
private lateinit var shellInit: ShellInit
private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository
+ private lateinit var desktopTasksLimiter: DesktopTasksLimiter
private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
private val shellExecutor = TestShellExecutor()
@@ -160,9 +162,12 @@ class DesktopTasksControllerTest : ShellTestCase() {
shellInit = Mockito.spy(ShellInit(testExecutor))
desktopModeTaskRepository = DesktopModeTaskRepository()
+ desktopTasksLimiter =
+ DesktopTasksLimiter(transitions, desktopModeTaskRepository, shellTaskOrganizer)
whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
+ whenever(enterDesktopTransitionHandler.moveToDesktop(any())).thenAnswer { Binder() }
whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
(i.arguments.first() as Rect).set(STABLE_BOUNDS)
@@ -203,7 +208,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
launchAdjacentController,
recentsTransitionHandler,
multiInstanceHelper,
- shellExecutor
+ shellExecutor,
+ Optional.of(desktopTasksLimiter),
)
}
@@ -409,6 +415,25 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun showDesktopApps_dontReorderMinimizedTask() {
+ val homeTask = setUpHomeTask()
+ val freeformTask = setUpFreeformTask()
+ val minimizedTask = setUpFreeformTask()
+ markTaskHidden(freeformTask)
+ markTaskHidden(minimizedTask)
+ desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct = getLatestWct(
+ type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(2)
+ // Reorder home and freeform task to top, don't reorder the minimized task
+ wct.assertReorderAt(index = 0, homeTask, toTop = true)
+ wct.assertReorderAt(index = 1, freeformTask, toTop = true)
+ }
+
+ @Test
fun getVisibleTaskCount_noTasks_returnsZero() {
assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
}
@@ -606,6 +631,24 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun moveToDesktop_bringsTasksOverLimit_dontShowBackTask() {
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ val homeTask = setUpHomeTask()
+ val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() }
+ val newTask = setUpFullscreenTask()
+
+ controller.moveToDesktop(newTask)
+
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.hierarchyOps.size).isEqualTo(taskLimit + 1) // visible tasks + home
+ wct.assertReorderAt(0, homeTask)
+ for (i in 1..<taskLimit) { // Skipping freeformTasks[0]
+ wct.assertReorderAt(index = i, task = freeformTasks[i])
+ }
+ wct.assertReorderAt(taskLimit, newTask)
+ }
+
+ @Test
fun moveToFullscreen_tdaFullscreen_windowingModeSetToUndefined() {
val task = setUpFreeformTask()
val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
@@ -659,6 +702,20 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun moveTaskToFront_bringsTasksOverLimit_minimizesBackTask() {
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ setUpHomeTask()
+ val freeformTasks = (1..taskLimit + 1).map { _ -> setUpFreeformTask() }
+
+ controller.moveTaskToFront(freeformTasks[0])
+
+ val wct = getLatestWct(type = TRANSIT_TO_FRONT)
+ assertThat(wct.hierarchyOps.size).isEqualTo(2) // move-to-front + minimize
+ wct.assertReorderAt(0, freeformTasks[0], toTop = true)
+ wct.assertReorderAt(1, freeformTasks[1], toTop = false)
+ }
+
+ @Test
fun moveToNextDisplay_noOtherDisplays() {
whenever(rootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(DEFAULT_DISPLAY))
val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -777,6 +834,38 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun handleRequest_fullscreenTaskToFreeform_underTaskLimit_dontMinimize() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+ val fullscreenTask = createFullscreenTask()
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ // Make sure we only reorder the new task to top (we don't reorder the old task to bottom)
+ assertThat(wct?.hierarchyOps?.size).isEqualTo(1)
+ wct!!.assertReorderAt(0, fullscreenTask, toTop = true)
+ }
+
+ @Test
+ fun handleRequest_fullscreenTaskToFreeform_bringsTasksOverLimit_otherTaskIsMinimized() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val fullscreenTask = createFullscreenTask()
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ // Make sure we reorder the new task to top, and the back task to the bottom
+ assertThat(wct!!.hierarchyOps.size).isEqualTo(2)
+ wct!!.assertReorderAt(0, fullscreenTask, toTop = true)
+ wct!!.assertReorderAt(1, freeformTasks[0], toTop = false)
+ }
+
+ @Test
fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -841,6 +930,22 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() }
+ freeformTasks.forEach { markTaskVisible(it) }
+ val newFreeformTask = createFreeformTask()
+
+ val wct =
+ controller.handleRequest(Binder(), createTransition(newFreeformTask, TRANSIT_OPEN))
+
+ assertThat(wct?.hierarchyOps?.size).isEqualTo(1)
+ wct!!.assertReorderAt(0, freeformTasks[0], toTop = false) // Reorder to the bottom
+ }
+
+ @Test
fun handleRequest_freeformTask_freeformNotVisible_returnSwitchToFullscreenWCT() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -1352,11 +1457,16 @@ private fun WindowContainerTransaction.assertIndexInBounds(index: Int) {
.isGreaterThan(index)
}
-private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) {
+private fun WindowContainerTransaction.assertReorderAt(
+ index: Int,
+ task: RunningTaskInfo,
+ toTop: Boolean? = null
+) {
assertIndexInBounds(index)
val op = hierarchyOps[index]
assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
assertThat(op.container).isEqualTo(task.token.asBinder())
+ toTop?.let { assertThat(op.toTop).isEqualTo(it) }
}
private fun WindowContainerTransaction.assertReorderSequence(vararg tasks: RunningTaskInfo) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
new file mode 100644
index 000000000000..38ea03471b07
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2024 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.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.os.Binder
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_BACK
+import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.transition.TransitionInfoBuilder
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.util.StubTransaction
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.quality.Strictness
+
+
+/**
+ * Test class for {@link DesktopTasksLimiter}
+ *
+ * Usage: atest WMShellUnitTests:DesktopTasksLimiterTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopTasksLimiterTest : ShellTestCase() {
+
+ @JvmField
+ @Rule
+ val setFlagsRule = SetFlagsRule()
+
+ @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
+ @Mock lateinit var transitions: Transitions
+
+ private lateinit var mockitoSession: StaticMockitoSession
+ private lateinit var desktopTasksLimiter: DesktopTasksLimiter
+ private lateinit var desktopTaskRepo: DesktopModeTaskRepository
+
+ @Before
+ fun setUp() {
+ mockitoSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT)
+ .spyStatic(DesktopModeStatus::class.java).startMocking()
+ `when`(DesktopModeStatus.isEnabled()).thenReturn(true)
+
+ desktopTaskRepo = DesktopModeTaskRepository()
+
+ desktopTasksLimiter = DesktopTasksLimiter(
+ transitions, desktopTaskRepo, shellTaskOrganizer)
+ }
+
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+ }
+
+ // Currently, the task limit can be overridden through an adb flag. This test ensures the limit
+ // hasn't been overridden.
+ @Test
+ fun getMaxTaskLimit_isSameAsConstant() {
+ assertThat(desktopTasksLimiter.getMaxTaskLimit()).isEqualTo(
+ DesktopModeStatus.DEFAULT_MAX_TASK_LIMIT)
+ }
+
+ @Test
+ fun addPendingMinimizeTransition_taskIsNotMinimized() {
+ val task = setUpFreeformTask()
+ markTaskHidden(task)
+
+ desktopTasksLimiter.addPendingMinimizeChange(Binder(), displayId = 1, taskId = task.taskId)
+
+ assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
+ }
+
+ @Test
+ fun onTransitionReady_noPendingTransition_taskIsNotMinimized() {
+ val task = setUpFreeformTask()
+ markTaskHidden(task)
+
+ desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+ Binder() /* transition */,
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
+ StubTransaction() /* startTransaction */,
+ StubTransaction() /* finishTransaction */)
+
+ assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
+ }
+
+ @Test
+ fun onTransitionReady_differentPendingTransition_taskIsNotMinimized() {
+ val pendingTransition = Binder()
+ val taskTransition = Binder()
+ val task = setUpFreeformTask()
+ markTaskHidden(task)
+ desktopTasksLimiter.addPendingMinimizeChange(
+ pendingTransition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+
+ desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+ taskTransition /* transition */,
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
+ StubTransaction() /* startTransaction */,
+ StubTransaction() /* finishTransaction */)
+
+ assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
+ }
+
+ @Test
+ fun onTransitionReady_pendingTransition_noTaskChange_taskVisible_taskIsNotMinimized() {
+ val transition = Binder()
+ val task = setUpFreeformTask()
+ markTaskVisible(task)
+ desktopTasksLimiter.addPendingMinimizeChange(
+ transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+
+ desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+ transition,
+ TransitionInfoBuilder(TRANSIT_OPEN).build(),
+ StubTransaction() /* startTransaction */,
+ StubTransaction() /* finishTransaction */)
+
+ assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
+ }
+
+ @Test
+ fun onTransitionReady_pendingTransition_noTaskChange_taskInvisible_taskIsMinimized() {
+ val transition = Binder()
+ val task = setUpFreeformTask()
+ markTaskHidden(task)
+ desktopTasksLimiter.addPendingMinimizeChange(
+ transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+
+ desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+ transition,
+ TransitionInfoBuilder(TRANSIT_OPEN).build(),
+ StubTransaction() /* startTransaction */,
+ StubTransaction() /* finishTransaction */)
+
+ assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
+ }
+
+ @Test
+ fun onTransitionReady_pendingTransition_changeTaskToBack_taskIsMinimized() {
+ val transition = Binder()
+ val task = setUpFreeformTask()
+ desktopTasksLimiter.addPendingMinimizeChange(
+ transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+
+ desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+ transition,
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
+ StubTransaction() /* startTransaction */,
+ StubTransaction() /* finishTransaction */)
+
+ assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
+ }
+
+ @Test
+ fun onTransitionReady_transitionMergedFromPending_taskIsMinimized() {
+ val mergedTransition = Binder()
+ val newTransition = Binder()
+ val task = setUpFreeformTask()
+ desktopTasksLimiter.addPendingMinimizeChange(
+ mergedTransition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+ desktopTasksLimiter.getTransitionObserver().onTransitionMerged(
+ mergedTransition, newTransition)
+
+ desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+ newTransition,
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
+ StubTransaction() /* startTransaction */,
+ StubTransaction() /* finishTransaction */)
+
+ assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
+ }
+
+ @Test
+ fun addAndGetMinimizeTaskChangesIfNeeded_tasksWithinLimit_noTaskMinimized() {
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ (1..<taskLimit).forEach { _ -> setUpFreeformTask() }
+
+ val wct = WindowContainerTransaction()
+ val minimizedTaskId =
+ desktopTasksLimiter.addAndGetMinimizeTaskChangesIfNeeded(
+ displayId = DEFAULT_DISPLAY,
+ wct = wct,
+ newFrontTaskInfo = setUpFreeformTask())
+
+ assertThat(minimizedTaskId).isNull()
+ assertThat(wct.hierarchyOps).isEmpty() // No reordering operations added
+ }
+
+ @Test
+ fun addAndGetMinimizeTaskChangesIfNeeded_tasksAboveLimit_backTaskMinimized() {
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ // The following list will be ordered bottom -> top, as the last task is moved to top last.
+ val tasks = (1..taskLimit).map { setUpFreeformTask() }
+
+ val wct = WindowContainerTransaction()
+ val minimizedTaskId =
+ desktopTasksLimiter.addAndGetMinimizeTaskChangesIfNeeded(
+ displayId = DEFAULT_DISPLAY,
+ wct = wct,
+ newFrontTaskInfo = setUpFreeformTask())
+
+ assertThat(minimizedTaskId).isEqualTo(tasks.first())
+ assertThat(wct.hierarchyOps.size).isEqualTo(1)
+ assertThat(wct.hierarchyOps[0].type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
+ assertThat(wct.hierarchyOps[0].toTop).isFalse() // Reorder to bottom
+ }
+
+ @Test
+ fun addAndGetMinimizeTaskChangesIfNeeded_nonMinimizedTasksWithinLimit_noTaskMinimized() {
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ val tasks = (1..taskLimit).map { setUpFreeformTask() }
+ desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = tasks[0].taskId)
+
+ val wct = WindowContainerTransaction()
+ val minimizedTaskId =
+ desktopTasksLimiter.addAndGetMinimizeTaskChangesIfNeeded(
+ displayId = 0,
+ wct = wct,
+ newFrontTaskInfo = setUpFreeformTask())
+
+ assertThat(minimizedTaskId).isNull()
+ assertThat(wct.hierarchyOps).isEmpty() // No reordering operations added
+ }
+
+ @Test
+ fun getTaskToMinimizeIfNeeded_tasksWithinLimit_returnsNull() {
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ val tasks = (1..taskLimit).map { setUpFreeformTask() }
+
+ val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded(
+ visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId })
+
+ assertThat(minimizedTask).isNull()
+ }
+
+ @Test
+ fun getTaskToMinimizeIfNeeded_tasksAboveLimit_returnsBackTask() {
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ val tasks = (1..taskLimit + 1).map { setUpFreeformTask() }
+
+ val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded(
+ visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId })
+
+ // first == front, last == back
+ assertThat(minimizedTask).isEqualTo(tasks.last())
+ }
+
+ @Test
+ fun getTaskToMinimizeIfNeeded_withNewTask_tasksAboveLimit_returnsBackTask() {
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ val tasks = (1..taskLimit).map { setUpFreeformTask() }
+
+ val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded(
+ visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId },
+ newTaskIdInFront = setUpFreeformTask().taskId)
+
+ // first == front, last == back
+ assertThat(minimizedTask).isEqualTo(tasks.last())
+ }
+
+ private fun setUpFreeformTask(
+ displayId: Int = DEFAULT_DISPLAY,
+ ): RunningTaskInfo {
+ val task = createFreeformTask(displayId)
+ `when`(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ desktopTaskRepo.addActiveTask(displayId, task.taskId)
+ desktopTaskRepo.addOrMoveFreeformTaskToTop(task.taskId)
+ return task
+ }
+
+ private fun markTaskVisible(task: RunningTaskInfo) {
+ desktopTaskRepo.updateVisibleFreeformTasks(
+ task.displayId,
+ task.taskId,
+ visible = true
+ )
+ }
+
+ private fun markTaskHidden(task: RunningTaskInfo) {
+ desktopTaskRepo.updateVisibleFreeformTasks(
+ task.displayId,
+ task.taskId,
+ visible = false
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index d7c46104b6b1..0434742c571b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -44,7 +44,6 @@ import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Handler;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -498,6 +497,31 @@ public class TaskViewTest extends ShellTestCase {
}
@Test
+ public void testStartRootTask_setsBoundsAndVisibility() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ TaskViewBase taskViewBase = mock(TaskViewBase.class);
+ Rect bounds = new Rect(0, 0, 100, 100);
+ when(taskViewBase.getCurrentBoundsOnScreen()).thenReturn(bounds);
+ mTaskViewTaskController.setTaskViewBase(taskViewBase);
+
+ // Surface created, but task not available so bounds / visibility isn't set
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ verify(mTaskViewTransitions, never()).updateVisibilityState(
+ eq(mTaskViewTaskController), eq(true));
+
+ // Make the task available
+ WindowContainerTransaction wct = mock(WindowContainerTransaction.class);
+ mTaskViewTaskController.startRootTask(mTaskInfo, mLeash, wct);
+
+ // Bounds got set
+ verify(wct).setBounds(any(WindowContainerToken.class), eq(bounds));
+ // Visibility & bounds state got set
+ verify(mTaskViewTransitions).updateVisibilityState(eq(mTaskViewTaskController), eq(true));
+ verify(mTaskViewTransitions).updateBoundsState(eq(mTaskViewTaskController), eq(bounds));
+ }
+
+ @Test
public void testTaskViewPrepareOpenAnimationSetsBoundsAndVisibility() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index fbc0db9c2850..d3e40f21db23 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.taskview;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.google.common.truth.Truth.assertThat;
@@ -208,6 +209,48 @@ public class TaskViewTransitionsTest extends ShellTestCase {
}
@Test
+ public void testReorderTask_movedToFrontTransaction() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ mTaskViewTransitions.reorderTaskViewTask(mTaskViewTaskController, true);
+ // Consume the pending transaction from order change
+ TaskViewTransitions.PendingTransition pending =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
+ assertThat(pending).isNotNull();
+ mTaskViewTransitions.startAnimation(pending.mClaimed,
+ mock(TransitionInfo.class),
+ new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(),
+ mock(Transitions.TransitionFinishCallback.class));
+
+ // Verify it was consumed
+ TaskViewTransitions.PendingTransition pending2 =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
+ assertThat(pending2).isNull();
+ }
+
+ @Test
+ public void testReorderTask_movedToBackTransaction() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ mTaskViewTransitions.reorderTaskViewTask(mTaskViewTaskController, false);
+ // Consume the pending transaction from order change
+ TaskViewTransitions.PendingTransition pending =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_BACK);
+ assertThat(pending).isNotNull();
+ mTaskViewTransitions.startAnimation(pending.mClaimed,
+ mock(TransitionInfo.class),
+ new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(),
+ mock(Transitions.TransitionFinishCallback.class));
+
+ // Verify it was consumed
+ TaskViewTransitions.PendingTransition pending2 =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_BACK);
+ assertThat(pending2).isNull();
+ }
+
+ @Test
public void test_startAnimation_setsTaskNotFound() {
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 2366917a0158..964d86e8bd35 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -73,6 +73,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArraySet;
import android.util.Pair;
import android.view.IRecentsAnimationRunner;
@@ -87,6 +88,7 @@ import android.window.RemoteTransitionStub;
import android.window.TransitionFilter;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowAnimationState;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -98,6 +100,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.R;
import com.android.internal.policy.TransitionAnimation;
+import com.android.systemui.shared.Flags;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
@@ -114,6 +117,7 @@ import com.android.wm.shell.sysui.ShellSharedConstants;
import com.android.wm.shell.util.StubTransaction;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -141,6 +145,9 @@ public class ShellTransitionTests extends ShellTestCase {
private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler();
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+ @Rule
+ public final SetFlagsRule setFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() {
doAnswer(invocation -> new Binder())
@@ -475,11 +482,97 @@ public class ShellTransitionTests extends ShellTestCase {
}
@Test
+ public void testRegisteredRemoteTransitionTakeover() {
+ Transitions transitions = createTestTransitions();
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IRemoteTransition testRemote = new RemoteTransitionStub() {
+ @Override
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ final Transitions.TransitionHandler takeoverHandler =
+ transitions.getHandlerForTakeover(token, info);
+
+ if (takeoverHandler == null) {
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
+ return;
+ }
+
+ takeoverHandler.takeOverAnimation(token, info, new SurfaceControl.Transaction(),
+ wct -> {
+ try {
+ finishCallback.onTransitionFinished(wct, null /* sct */);
+ } catch (RemoteException e) {
+ // Fail
+ }
+ }, new WindowAnimationState[info.getChanges().size()]);
+ }
+ };
+ final boolean[] takeoverRemoteCalled = new boolean[]{false};
+ IRemoteTransition testTakeoverRemote = new RemoteTransitionStub() {
+ @Override
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
+ IRemoteTransitionFinishedCallback finishCallback) {}
+
+ @Override
+ public void takeOverAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ IRemoteTransitionFinishedCallback finishCallback, WindowAnimationState[] states)
+ throws RemoteException {
+ takeoverRemoteCalled[0] = true;
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
+ }
+ };
+
+ TransitionFilter filter = new TransitionFilter();
+ filter.mRequirements =
+ new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
+ filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+
+ transitions.registerRemote(filter, new RemoteTransition(testRemote, "Test"));
+ transitions.registerRemoteForTakeover(
+ filter, new RemoteTransition(testTakeoverRemote, "Test"));
+ mMainExecutor.flushAll();
+
+ // Takeover shouldn't happen when the flag is disabled.
+ setFlagsRule.disableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY);
+ IBinder transitToken = new Binder();
+ transitions.requestStartTransition(transitToken,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ transitions.onTransitionReady(transitToken, info, new StubTransaction(),
+ new StubTransaction());
+ assertEquals(0, mDefaultHandler.activeCount());
+ assertFalse(takeoverRemoteCalled[0]);
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any());
+
+ // Takeover should happen when the flag is enabled.
+ setFlagsRule.enableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY);
+ transitions.requestStartTransition(transitToken,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ info = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ transitions.onTransitionReady(transitToken, info, new StubTransaction(),
+ new StubTransaction());
+ assertEquals(0, mDefaultHandler.activeCount());
+ assertTrue(takeoverRemoteCalled[0]);
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ verify(mOrganizer, times(2)).finishTransition(eq(transitToken), any());
+ }
+
+ @Test
public void testOneShotRemoteHandler() {
Transitions transitions = createTestTransitions();
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final boolean[] remoteCalled = new boolean[]{false};
+ final boolean[] takeoverRemoteCalled = new boolean[]{false};
final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
IRemoteTransition testRemote = new RemoteTransitionStub() {
@Override
@@ -489,12 +582,22 @@ public class ShellTransitionTests extends ShellTestCase {
remoteCalled[0] = true;
finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
}
+
+ @Override
+ public void takeOverAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ IRemoteTransitionFinishedCallback finishCallback, WindowAnimationState[] states)
+ throws RemoteException {
+ takeoverRemoteCalled[0] = true;
+ finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
+ }
};
final int transitType = TRANSIT_FIRST_CUSTOM + 1;
OneShotRemoteHandler oneShot = new OneShotRemoteHandler(mMainExecutor,
new RemoteTransition(testRemote, "Test"));
+
// Verify that it responds to the remote but not other things.
IBinder transitToken = new Binder();
assertNotNull(oneShot.handleRequest(transitToken,
@@ -505,6 +608,7 @@ public class ShellTransitionTests extends ShellTestCase {
Transitions.TransitionFinishCallback testFinish =
mock(Transitions.TransitionFinishCallback.class);
+
// Verify that it responds to animation properly
oneShot.setTransition(transitToken);
IBinder anotherToken = new Binder();
@@ -514,6 +618,16 @@ public class ShellTransitionTests extends ShellTestCase {
assertTrue(oneShot.startAnimation(transitToken, new TransitionInfo(transitType, 0),
new StubTransaction(), new StubTransaction(),
testFinish));
+ assertTrue(remoteCalled[0]);
+
+ // Verify that it handles takeovers properly
+ IBinder newToken = new Binder();
+ oneShot.setTransition(newToken);
+ assertFalse(oneShot.takeOverAnimation(transitToken, new TransitionInfo(transitType, 0),
+ new StubTransaction(), testFinish, new WindowAnimationState[0]));
+ assertTrue(oneShot.takeOverAnimation(newToken, new TransitionInfo(transitType, 0),
+ new StubTransaction(), testFinish, new WindowAnimationState[0]));
+ assertTrue(takeoverRemoteCalled[0]);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 8e9619dc1430..7d19f3cbf659 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -28,6 +28,9 @@ import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.os.Handler
import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -97,6 +100,10 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Rule
val setFlagsRule = SetFlagsRule()
+ @JvmField
+ @Rule
+ val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
@Mock private lateinit var mockDesktopModeWindowDecorFactory:
DesktopModeWindowDecoration.Factory
@Mock private lateinit var mockMainHandler: Handler
@@ -306,6 +313,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_IMMERSIVE_HANDLE_HIDING)
fun testRelayoutRunsWhenStatusBarsInsetsSourceVisibilityChanges() {
val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM, focused = true)
val decoration = setUpMockDecorationForTask(task)
@@ -326,6 +334,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_IMMERSIVE_HANDLE_HIDING)
fun testRelayoutDoesNotRunWhenNonStatusBarsInsetsSourceVisibilityChanges() {
val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM, focused = true)
val decoration = setUpMockDecorationForTask(task)
@@ -346,6 +355,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_IMMERSIVE_HANDLE_HIDING)
fun testRelayoutDoesNotRunWhenNonStatusBarsInsetSourceVisibilityDoesNotChange() {
val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM, focused = true)
val decoration = setUpMockDecorationForTask(task)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index f9b5882f7ad5..608f74b95280 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.windowdecor;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
import static com.google.common.truth.Truth.assertThat;
@@ -44,6 +45,7 @@ import android.view.Choreographer;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
+import android.view.WindowManager;
import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
@@ -187,7 +189,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* applyStartTransactionOnDraw= */ true,
/* shouldSetTaskPositionAndCrop */ false);
- assertThat(relayoutParams.mAllowCaptionInputFallthrough).isTrue();
+ assertThat(relayoutParams.hasInputFeatureSpy()).isTrue();
}
@Test
@@ -204,7 +206,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* applyStartTransactionOnDraw= */ true,
/* shouldSetTaskPositionAndCrop */ false);
- assertThat(relayoutParams.mAllowCaptionInputFallthrough).isFalse();
+ assertThat(relayoutParams.hasInputFeatureSpy()).isFalse();
}
@Test
@@ -220,7 +222,55 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* applyStartTransactionOnDraw= */ true,
/* shouldSetTaskPositionAndCrop */ false);
- assertThat(relayoutParams.mAllowCaptionInputFallthrough).isFalse();
+ assertThat(relayoutParams.hasInputFeatureSpy()).isFalse();
+ }
+
+ @Test
+ public void updateRelayoutParams_freeform_inputChannelNeeded() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false);
+
+ assertThat(hasNoInputChannelFeature(relayoutParams)).isFalse();
+ }
+
+ @Test
+ public void updateRelayoutParams_fullscreen_inputChannelNotNeeded() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false);
+
+ assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue();
+ }
+
+ @Test
+ public void updateRelayoutParams_multiwindow_inputChannelNotNeeded() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false);
+
+ assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue();
}
private void fillRoundedCornersResources(int fillValue) {
@@ -268,4 +318,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
return taskInfo;
}
+
+ private static boolean hasNoInputChannelFeature(RelayoutParams params) {
+ return (params.mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL)
+ != 0;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
index 847c2dd77d0a..87425915fbf7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.wm.shell.windowdecor
+import android.graphics.Bitmap
import android.graphics.Rect
-import android.graphics.drawable.Drawable
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Display
@@ -60,7 +60,7 @@ class ResizeVeilTest : ShellTestCase() {
@Mock
private lateinit var mockDisplayController: DisplayController
@Mock
- private lateinit var mockAppIcon: Drawable
+ private lateinit var mockAppIcon: Bitmap
@Mock
private lateinit var mockDisplay: Display
@Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index a9f44929fc64..48ac1e5717aa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -197,7 +197,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
rectAfterEnd.top += 20
rectAfterEnd.bottom += 20
- verify(mockDesktopWindowDecoration, never()).createResizeVeil()
+ verify(mockDesktopWindowDecoration, never()).showResizeVeil(any())
verify(mockDesktopWindowDecoration, never()).hideResizeVeil()
verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
diff --git a/libs/hostgraphics/ADisplay.cpp b/libs/hostgraphics/ADisplay.cpp
index 9cc1f40184e3..58fa08281a61 100644
--- a/libs/hostgraphics/ADisplay.cpp
+++ b/libs/hostgraphics/ADisplay.cpp
@@ -94,14 +94,14 @@ namespace android {
int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
// This is running on host, so there are no physical displays available.
// Create 1 fake display instead.
- DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>(
- malloc(sizeof(DisplayImpl*) + sizeof(DisplayImpl)));
+ DisplayImpl** const impls =
+ reinterpret_cast<DisplayImpl**>(malloc(sizeof(DisplayImpl*) + sizeof(DisplayImpl)));
DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + 1);
- displayData[0] = DisplayImpl{ADisplayType::DISPLAY_TYPE_INTERNAL,
- ADataSpace::ADATASPACE_UNKNOWN,
- AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
- DisplayConfigImpl()};
+ displayData[0] =
+ DisplayImpl{ADisplayType::DISPLAY_TYPE_INTERNAL, ADataSpace::ADATASPACE_UNKNOWN,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ DisplayConfigImpl()};
impls[0] = displayData;
*outDisplays = reinterpret_cast<ADisplay**>(impls);
return 1;
diff --git a/libs/hostgraphics/Fence.cpp b/libs/hostgraphics/Fence.cpp
index 9e54816651c4..4383bf02a00e 100644
--- a/libs/hostgraphics/Fence.cpp
+++ b/libs/hostgraphics/Fence.cpp
@@ -20,4 +20,4 @@ namespace android {
const sp<Fence> Fence::NO_FENCE = sp<Fence>(new Fence);
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/libs/hostgraphics/HostBufferQueue.cpp b/libs/hostgraphics/HostBufferQueue.cpp
index ec304378c6c4..7e14b88a47fa 100644
--- a/libs/hostgraphics/HostBufferQueue.cpp
+++ b/libs/hostgraphics/HostBufferQueue.cpp
@@ -15,18 +15,26 @@
*/
#include <gui/BufferQueue.h>
+#include <system/window.h>
namespace android {
class HostBufferQueue : public IGraphicBufferProducer, public IGraphicBufferConsumer {
public:
- HostBufferQueue() : mWidth(0), mHeight(0) { }
+ HostBufferQueue() : mWidth(0), mHeight(0) {}
- virtual status_t setConsumerIsProtected(bool isProtected) { return OK; }
+ // Consumer
+ virtual status_t setConsumerIsProtected(bool isProtected) {
+ return OK;
+ }
- virtual status_t detachBuffer(int slot) { return OK; }
+ virtual status_t detachBuffer(int slot) {
+ return OK;
+ }
- virtual status_t getReleasedBuffers(uint64_t* slotMask) { return OK; }
+ virtual status_t getReleasedBuffers(uint64_t* slotMask) {
+ return OK;
+ }
virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) {
mWidth = w;
@@ -35,22 +43,54 @@ public:
return OK;
}
- virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) { return OK; }
+ virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) {
+ return OK;
+ }
- virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) { return OK; }
+ virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) {
+ return OK;
+ }
- virtual status_t discardFreeBuffers() { return OK; }
+ virtual status_t discardFreeBuffers() {
+ return OK;
+ }
virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
- uint64_t maxFrameNumber = 0) {
+ uint64_t maxFrameNumber = 0) {
buffer->mGraphicBuffer = mBuffer;
buffer->mSlot = 0;
return OK;
}
- virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) { return OK; }
+ virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
+ return OK;
+ }
+
+ virtual status_t setConsumerUsageBits(uint64_t usage) {
+ return OK;
+ }
+
+ // Producer
+ virtual int query(int what, int* value) {
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ *value = mWidth;
+ break;
+ case NATIVE_WINDOW_HEIGHT:
+ *value = mHeight;
+ break;
+ default:
+ *value = 0;
+ break;
+ }
+ return OK;
+ }
+
+ virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+ *buf = mBuffer;
+ return OK;
+ }
- virtual status_t setConsumerUsageBits(uint64_t usage) { return OK; }
private:
sp<GraphicBuffer> mBuffer;
uint32_t mWidth;
@@ -58,8 +98,7 @@ private:
};
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
- sp<IGraphicBufferConsumer>* outConsumer) {
-
+ sp<IGraphicBufferConsumer>* outConsumer) {
sp<HostBufferQueue> obj(new HostBufferQueue());
*outProducer = obj;
diff --git a/libs/hostgraphics/PublicFormat.cpp b/libs/hostgraphics/PublicFormat.cpp
index af6d2738c801..2a2eec63467c 100644
--- a/libs/hostgraphics/PublicFormat.cpp
+++ b/libs/hostgraphics/PublicFormat.cpp
@@ -30,4 +30,4 @@ PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace d
return static_cast<PublicFormat>(format);
}
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/libs/hostgraphics/gui/BufferItem.h b/libs/hostgraphics/gui/BufferItem.h
index 01409e19c715..e95a9231dfaf 100644
--- a/libs/hostgraphics/gui/BufferItem.h
+++ b/libs/hostgraphics/gui/BufferItem.h
@@ -17,16 +17,15 @@
#ifndef ANDROID_GUI_BUFFERITEM_H
#define ANDROID_GUI_BUFFERITEM_H
+#include <system/graphics.h>
#include <ui/Fence.h>
#include <ui/Rect.h>
-
-#include <system/graphics.h>
-
#include <utils/StrongPointer.h>
namespace android {
class Fence;
+
class GraphicBuffer;
// The only thing we need here for layoutlib is mGraphicBuffer. The rest of the fields are added
@@ -37,6 +36,7 @@ public:
enum { INVALID_BUFFER_SLOT = -1 };
BufferItem() : mGraphicBuffer(nullptr), mFence(Fence::NO_FENCE) {}
+
~BufferItem() {}
sp<GraphicBuffer> mGraphicBuffer;
@@ -60,6 +60,6 @@ public:
bool mTransformToDisplayInverse;
};
-}
+} // namespace android
#endif // ANDROID_GUI_BUFFERITEM_H
diff --git a/libs/hostgraphics/gui/BufferItemConsumer.h b/libs/hostgraphics/gui/BufferItemConsumer.h
index 707b313eb102..c25941151800 100644
--- a/libs/hostgraphics/gui/BufferItemConsumer.h
+++ b/libs/hostgraphics/gui/BufferItemConsumer.h
@@ -17,32 +17,30 @@
#ifndef ANDROID_GUI_BUFFERITEMCONSUMER_H
#define ANDROID_GUI_BUFFERITEMCONSUMER_H
-#include <utils/RefBase.h>
-
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferConsumer.h>
+#include <utils/RefBase.h>
namespace android {
class BufferItemConsumer : public ConsumerBase {
public:
- BufferItemConsumer(
- const sp<IGraphicBufferConsumer>& consumer,
- uint64_t consumerUsage,
- int bufferCount,
- bool controlledByApp) : mConsumer(consumer) {
- }
+ BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount, bool controlledByApp)
+ : mConsumer(consumer) {}
- status_t acquireBuffer(BufferItem *item, nsecs_t presentWhen, bool waitForFence = true) {
+ status_t acquireBuffer(BufferItem* item, nsecs_t presentWhen, bool waitForFence = true) {
return mConsumer->acquireBuffer(item, presentWhen, 0);
}
- status_t releaseBuffer(
- const BufferItem &item, const sp<Fence>& releaseFence = Fence::NO_FENCE) { return OK; }
+ status_t releaseBuffer(const BufferItem& item,
+ const sp<Fence>& releaseFence = Fence::NO_FENCE) {
+ return OK;
+ }
- void setName(const String8& name) { }
+ void setName(const String8& name) {}
- void setFrameAvailableListener(const wp<FrameAvailableListener>& listener) { }
+ void setFrameAvailableListener(const wp<FrameAvailableListener>& listener) {}
status_t setDefaultBufferSize(uint32_t width, uint32_t height) {
return mConsumer->setDefaultBufferSize(width, height);
@@ -56,16 +54,23 @@ public:
return mConsumer->setDefaultBufferDataSpace(defaultDataSpace);
}
- void abandon() { }
+ void abandon() {}
- status_t detachBuffer(int slot) { return OK; }
+ status_t detachBuffer(int slot) {
+ return OK;
+ }
+
+ status_t discardFreeBuffers() {
+ return OK;
+ }
- status_t discardFreeBuffers() { return OK; }
+ void freeBufferLocked(int slotIndex) {}
- void freeBufferLocked(int slotIndex) { }
+ status_t addReleaseFenceLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
+ const sp<Fence>& fence) {
+ return OK;
+ }
- status_t addReleaseFenceLocked(
- int slot, const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) { return OK; }
private:
sp<IGraphicBufferConsumer> mConsumer;
};
diff --git a/libs/hostgraphics/gui/BufferQueue.h b/libs/hostgraphics/gui/BufferQueue.h
index aa3e7268e11c..67a8c00fd267 100644
--- a/libs/hostgraphics/gui/BufferQueue.h
+++ b/libs/hostgraphics/gui/BufferQueue.h
@@ -29,7 +29,7 @@ public:
enum { NO_BUFFER_AVAILABLE = IGraphicBufferConsumer::NO_BUFFER_AVAILABLE };
static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
- sp<IGraphicBufferConsumer>* outConsumer);
+ sp<IGraphicBufferConsumer>* outConsumer);
};
} // namespace android
diff --git a/libs/hostgraphics/gui/ConsumerBase.h b/libs/hostgraphics/gui/ConsumerBase.h
index 9002953c0848..7f7309e8a3a8 100644
--- a/libs/hostgraphics/gui/ConsumerBase.h
+++ b/libs/hostgraphics/gui/ConsumerBase.h
@@ -18,7 +18,6 @@
#define ANDROID_GUI_CONSUMERBASE_H
#include <gui/BufferItem.h>
-
#include <utils/RefBase.h>
namespace android {
@@ -28,10 +27,11 @@ public:
struct FrameAvailableListener : public virtual RefBase {
// See IConsumerListener::onFrame{Available,Replaced}
virtual void onFrameAvailable(const BufferItem& item) = 0;
+
virtual void onFrameReplaced(const BufferItem& /* item */) {}
};
};
} // namespace android
-#endif // ANDROID_GUI_CONSUMERBASE_H \ No newline at end of file
+#endif // ANDROID_GUI_CONSUMERBASE_H
diff --git a/libs/hostgraphics/gui/IGraphicBufferConsumer.h b/libs/hostgraphics/gui/IGraphicBufferConsumer.h
index 9eb67b218800..14ac4fe71cc8 100644
--- a/libs/hostgraphics/gui/IGraphicBufferConsumer.h
+++ b/libs/hostgraphics/gui/IGraphicBufferConsumer.h
@@ -16,16 +16,16 @@
#pragma once
-#include <utils/RefBase.h>
-
#include <ui/PixelFormat.h>
-
#include <utils/Errors.h>
+#include <utils/RefBase.h>
namespace android {
class BufferItem;
+
class Fence;
+
class GraphicBuffer;
class IGraphicBufferConsumer : virtual public RefBase {
@@ -62,4 +62,4 @@ public:
virtual status_t discardFreeBuffers() = 0;
};
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/libs/hostgraphics/gui/IGraphicBufferProducer.h b/libs/hostgraphics/gui/IGraphicBufferProducer.h
index a1efd0bcfa4c..8fd8590d10d7 100644
--- a/libs/hostgraphics/gui/IGraphicBufferProducer.h
+++ b/libs/hostgraphics/gui/IGraphicBufferProducer.h
@@ -17,9 +17,8 @@
#ifndef ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H
#define ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H
-#include <utils/RefBase.h>
-
#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
namespace android {
@@ -31,6 +30,10 @@ public:
// Disconnect any API originally connected from the process calling disconnect.
AllLocal
};
+
+ virtual int query(int what, int* value) = 0;
+
+ virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0;
};
} // namespace android
diff --git a/libs/hostgraphics/gui/Surface.h b/libs/hostgraphics/gui/Surface.h
index 36d8fba0d61a..2774f89cb54c 100644
--- a/libs/hostgraphics/gui/Surface.h
+++ b/libs/hostgraphics/gui/Surface.h
@@ -17,25 +17,36 @@
#ifndef ANDROID_GUI_SURFACE_H
#define ANDROID_GUI_SURFACE_H
-#include <gui/IGraphicBufferProducer.h>
+#include <system/window.h>
#include <ui/ANativeObjectBase.h>
#include <utils/RefBase.h>
-#include <system/window.h>
+
+#include "gui/IGraphicBufferProducer.h"
namespace android {
class Surface : public ANativeObjectBase<ANativeWindow, Surface, RefBase> {
public:
- explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer,
- bool controlledByApp = false) {
+ explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false)
+ : mBufferProducer(bufferProducer) {
ANativeWindow::perform = hook_perform;
+ ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
+ ANativeWindow::query = hook_query;
}
- static bool isValid(const sp<Surface>& surface) { return surface != nullptr; }
+
+ static bool isValid(const sp<Surface>& surface) {
+ return surface != nullptr;
+ }
+
void allocateBuffers() {}
- uint64_t getNextFrameNumber() const { return 0; }
+ uint64_t getNextFrameNumber() const {
+ return 0;
+ }
- int setScalingMode(int mode) { return 0; }
+ int setScalingMode(int mode) {
+ return 0;
+ }
virtual int disconnect(int api,
IGraphicBufferProducer::DisconnectMode mode =
@@ -47,24 +58,88 @@ public:
// TODO: implement this
return 0;
}
- virtual int unlockAndPost() { return 0; }
- virtual int query(int what, int* value) const { return 0; }
+
+ virtual int unlockAndPost() {
+ return 0;
+ }
+
+ virtual int query(int what, int* value) const {
+ return mBufferProducer->query(what, value);
+ }
+
+ status_t setDequeueTimeout(nsecs_t timeout) {
+ return OK;
+ }
+
+ nsecs_t getLastDequeueStartTime() const {
+ return 0;
+ }
virtual void destroy() {}
- int getBuffersDataSpace() { return 0; }
+ int getBuffersDataSpace() {
+ return 0;
+ }
protected:
virtual ~Surface() {}
- static int hook_perform(ANativeWindow* window, int operation, ...) { return 0; }
+ static int hook_perform(ANativeWindow* window, int operation, ...) {
+ va_list args;
+ va_start(args, operation);
+ Surface* c = getSelf(window);
+ int result = c->perform(operation, args);
+ va_end(args);
+ return result;
+ }
+
+ static int hook_query(const ANativeWindow* window, int what, int* value) {
+ const Surface* c = getSelf(window);
+ return c->query(what, value);
+ }
+
+ static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd) {
+ Surface* c = getSelf(window);
+ return c->dequeueBuffer(buffer, fenceFd);
+ }
+
+ virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) {
+ mBufferProducer->requestBuffer(0, &mBuffer);
+ *buffer = mBuffer.get();
+ return OK;
+ }
+
+ virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
+ return 0;
+ }
+
+ virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
+ return 0;
+ }
+
+ virtual int perform(int operation, va_list args) {
+ return 0;
+ }
+
+ virtual int setSwapInterval(int interval) {
+ return 0;
+ }
+
+ virtual int setBufferCount(int bufferCount) {
+ return 0;
+ }
private:
// can't be copied
Surface& operator=(const Surface& rhs);
+
Surface(const Surface& rhs);
+
+ const sp<IGraphicBufferProducer> mBufferProducer;
+ sp<GraphicBuffer> mBuffer;
};
} // namespace android
-#endif // ANDROID_GUI_SURFACE_H
+#endif // ANDROID_GUI_SURFACE_H
diff --git a/libs/hostgraphics/ui/Fence.h b/libs/hostgraphics/ui/Fence.h
index 04d535c3a211..187c3116f61c 100644
--- a/libs/hostgraphics/ui/Fence.h
+++ b/libs/hostgraphics/ui/Fence.h
@@ -17,8 +17,8 @@
#ifndef ANDROID_FENCE_H
#define ANDROID_FENCE_H
-#include <utils/String8.h>
#include <utils/RefBase.h>
+#include <utils/String8.h>
typedef int64_t nsecs_t;
@@ -26,11 +26,14 @@ namespace android {
class Fence : public LightRefBase<Fence> {
public:
- Fence() { }
- Fence(int) { }
+ Fence() {}
+
+ Fence(int) {}
+
static const sp<Fence> NO_FENCE;
static constexpr nsecs_t SIGNAL_TIME_PENDING = INT64_MAX;
static constexpr nsecs_t SIGNAL_TIME_INVALID = -1;
+
static sp<Fence> merge(const char* name, const sp<Fence>& f1, const sp<Fence>& f2) {
return NO_FENCE;
}
@@ -40,16 +43,22 @@ public:
}
enum class Status {
- Invalid, // Fence is invalid
- Unsignaled, // Fence is valid but has not yet signaled
- Signaled, // Fence is valid and has signaled
+ Invalid, // Fence is invalid
+ Unsignaled, // Fence is valid but has not yet signaled
+ Signaled, // Fence is valid and has signaled
};
- status_t wait(int timeout) { return OK; }
+ status_t wait(int timeout) {
+ return OK;
+ }
- status_t waitForever(const char* logname) { return OK; }
+ status_t waitForever(const char* logname) {
+ return OK;
+ }
- int dup() const { return 0; }
+ int dup() const {
+ return 0;
+ }
inline Status getStatus() {
// The sync_wait call underlying wait() has been measured to be
diff --git a/libs/hostgraphics/ui/GraphicBuffer.h b/libs/hostgraphics/ui/GraphicBuffer.h
index ac88e44dbc65..cda45e4660ca 100644
--- a/libs/hostgraphics/ui/GraphicBuffer.h
+++ b/libs/hostgraphics/ui/GraphicBuffer.h
@@ -19,31 +19,51 @@
#include <stdint.h>
#include <sys/types.h>
-
-#include <vector>
-
+#include <ui/ANativeObjectBase.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
-
#include <utils/RefBase.h>
+#include <vector>
+
namespace android {
-class GraphicBuffer : virtual public RefBase {
+class GraphicBuffer : public ANativeObjectBase<ANativeWindowBuffer, GraphicBuffer, RefBase> {
public:
- GraphicBuffer(uint32_t w, uint32_t h):width(w),height(h) {
- data.resize(w*h);
+ GraphicBuffer(uint32_t w, uint32_t h) {
+ data.resize(w * h);
+ reserved[0] = data.data();
+ width = w;
+ height = h;
+ }
+
+ uint32_t getWidth() const {
+ return static_cast<uint32_t>(width);
+ }
+
+ uint32_t getHeight() const {
+ return static_cast<uint32_t>(height);
+ }
+
+ uint32_t getStride() const {
+ return static_cast<uint32_t>(width);
+ }
+
+ uint64_t getUsage() const {
+ return 0;
}
- uint32_t getWidth() const { return static_cast<uint32_t>(width); }
- uint32_t getHeight() const { return static_cast<uint32_t>(height); }
- uint32_t getStride() const { return static_cast<uint32_t>(width); }
- uint64_t getUsage() const { return 0; }
- PixelFormat getPixelFormat() const { return PIXEL_FORMAT_RGBA_8888; }
- //uint32_t getLayerCount() const { return static_cast<uint32_t>(layerCount); }
- Rect getBounds() const { return Rect(width, height); }
- status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect,
- android_ycbcr *ycbcr, int fenceFd) { return OK; }
+ PixelFormat getPixelFormat() const {
+ return PIXEL_FORMAT_RGBA_8888;
+ }
+
+ Rect getBounds() const {
+ return Rect(width, height);
+ }
+
+ status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect, android_ycbcr* ycbcr, int fenceFd) {
+ return OK;
+ }
status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd,
int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr) {
@@ -51,11 +71,11 @@ public:
return OK;
}
- status_t unlockAsync(int *fenceFd) { return OK; }
+ status_t unlockAsync(int* fenceFd) {
+ return OK;
+ }
private:
- uint32_t width;
- uint32_t height;
std::vector<uint32_t> data;
};
diff --git a/libs/hwui/SkiaInterpolator.cpp b/libs/hwui/SkiaInterpolator.cpp
index c67b135855f7..5a45ad9085e7 100644
--- a/libs/hwui/SkiaInterpolator.cpp
+++ b/libs/hwui/SkiaInterpolator.cpp
@@ -20,6 +20,7 @@
#include "include/core/SkTypes.h"
#include <cstdlib>
+#include <cstring>
#include <log/log.h>
typedef int Dot14;
diff --git a/libs/hwui/effects/GainmapRenderer.cpp b/libs/hwui/effects/GainmapRenderer.cpp
index 3ebf7d19202d..0a30c6c14c4c 100644
--- a/libs/hwui/effects/GainmapRenderer.cpp
+++ b/libs/hwui/effects/GainmapRenderer.cpp
@@ -32,6 +32,8 @@
#include "src/core/SkColorFilterPriv.h"
#include "src/core/SkImageInfoPriv.h"
#include "src/core/SkRuntimeEffectPriv.h"
+
+#include <cmath>
#endif
namespace android::uirenderer {
@@ -206,12 +208,12 @@ private:
void setupGenericUniforms(const sk_sp<const SkImage>& gainmapImage,
const SkGainmapInfo& gainmapInfo) {
- const SkColor4f logRatioMin({sk_float_log(gainmapInfo.fGainmapRatioMin.fR),
- sk_float_log(gainmapInfo.fGainmapRatioMin.fG),
- sk_float_log(gainmapInfo.fGainmapRatioMin.fB), 1.f});
- const SkColor4f logRatioMax({sk_float_log(gainmapInfo.fGainmapRatioMax.fR),
- sk_float_log(gainmapInfo.fGainmapRatioMax.fG),
- sk_float_log(gainmapInfo.fGainmapRatioMax.fB), 1.f});
+ const SkColor4f logRatioMin({std::log(gainmapInfo.fGainmapRatioMin.fR),
+ std::log(gainmapInfo.fGainmapRatioMin.fG),
+ std::log(gainmapInfo.fGainmapRatioMin.fB), 1.f});
+ const SkColor4f logRatioMax({std::log(gainmapInfo.fGainmapRatioMax.fR),
+ std::log(gainmapInfo.fGainmapRatioMax.fG),
+ std::log(gainmapInfo.fGainmapRatioMax.fB), 1.f});
const int noGamma = gainmapInfo.fGainmapGamma.fR == 1.f &&
gainmapInfo.fGainmapGamma.fG == 1.f &&
gainmapInfo.fGainmapGamma.fB == 1.f;
@@ -248,10 +250,10 @@ private:
float W = 0.f;
if (targetHdrSdrRatio > mGainmapInfo.fDisplayRatioSdr) {
if (targetHdrSdrRatio < mGainmapInfo.fDisplayRatioHdr) {
- W = (sk_float_log(targetHdrSdrRatio) -
- sk_float_log(mGainmapInfo.fDisplayRatioSdr)) /
- (sk_float_log(mGainmapInfo.fDisplayRatioHdr) -
- sk_float_log(mGainmapInfo.fDisplayRatioSdr));
+ W = (std::log(targetHdrSdrRatio) -
+ std::log(mGainmapInfo.fDisplayRatioSdr)) /
+ (std::log(mGainmapInfo.fDisplayRatioHdr) -
+ std::log(mGainmapInfo.fDisplayRatioSdr));
} else {
W = 1.f;
}
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index b87002371775..8e07a2f31de1 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -15,14 +15,18 @@
*/
#include "ShaderCache.h"
+
#include <GrDirectContext.h>
#include <SkData.h>
#include <gui/TraceUtils.h>
#include <log/log.h>
#include <openssl/sha.h>
+
#include <algorithm>
#include <array>
+#include <mutex>
#include <thread>
+
#include "FileBlobCache.h"
#include "Properties.h"
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 66e089627a7b..8bb11badb607 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -1010,7 +1010,15 @@ void CanvasContext::destroyHardwareResources() {
}
void CanvasContext::onContextDestroyed() {
- destroyHardwareResources();
+ // We don't want to destroyHardwareResources as that will invalidate display lists which
+ // the client may not be expecting. Instead just purge all scratch resources
+ if (mRenderPipeline->isContextReady()) {
+ freePrefetchedLayers();
+ for (const sp<RenderNode>& node : mRenderNodes) {
+ node->destroyLayers();
+ }
+ mRenderPipeline->onDestroyHardwareResources();
+ }
}
DeferredLayerUpdater* CanvasContext::createTextureLayer() {
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index 6993d5240187..7a155c583fd4 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -45,7 +45,7 @@ void HintSessionWrapper::HintSessionBinding::init() {
LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
BIND_APH_METHOD(getManager);
- BIND_APH_METHOD(createSession);
+ BIND_APH_METHOD(createSessionInternal);
BIND_APH_METHOD(closeSession);
BIND_APH_METHOD(updateTargetWorkDuration);
BIND_APH_METHOD(reportActualWorkDuration);
@@ -122,7 +122,8 @@ bool HintSessionWrapper::init() {
int64_t targetDurationNanos =
mLastTargetWorkDuration == 0 ? kDefaultTargetDuration : mLastTargetWorkDuration;
mHintSessionFuture = CommonPool::async([=, this, tids = mPermanentSessionTids] {
- return mBinding->createSession(manager, tids.data(), tids.size(), targetDurationNanos);
+ return mBinding->createSessionInternal(manager, tids.data(), tids.size(),
+ targetDurationNanos, SessionTag::HWUI);
});
return false;
}
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index 14e7a53fd94f..859cc57dea9f 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -17,6 +17,7 @@
#pragma once
#include <android/performance_hint.h>
+#include <private/performance_hint_private.h>
#include <future>
#include <optional>
@@ -80,9 +81,10 @@ private:
virtual ~HintSessionBinding() = default;
virtual void init();
APerformanceHintManager* (*getManager)();
- APerformanceHintSession* (*createSession)(APerformanceHintManager* manager,
- const int32_t* tids, size_t tidCount,
- int64_t defaultTarget) = nullptr;
+ APerformanceHintSession* (*createSessionInternal)(APerformanceHintManager* manager,
+ const int32_t* tids, size_t tidCount,
+ int64_t defaultTarget,
+ SessionTag tag) = nullptr;
void (*closeSession)(APerformanceHintSession* session) = nullptr;
void (*updateTargetWorkDuration)(APerformanceHintSession* session,
int64_t targetDuration) = nullptr;
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index a8e85475aff0..0f29613cad33 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -322,11 +322,16 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window
return false;
}
- err = native_window_set_buffer_count(window, windowInfo.bufferCount);
- if (err != 0) {
- ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)",
- windowInfo.bufferCount, strerror(-err), err);
- return false;
+ // If bufferCount == 1 then we're in shared buffer mode and we cannot actually call
+ // set_buffer_count, it'll just fail.
+ if (windowInfo.bufferCount > 1) {
+ err = native_window_set_buffer_count(window, windowInfo.bufferCount);
+ if (err != 0) {
+ ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s "
+ "(%d)",
+ windowInfo.bufferCount, strerror(-err), err);
+ return false;
+ }
}
err = native_window_set_usage(window, windowInfo.windowUsageFlags);
diff --git a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
index c16602c29e2a..a8db0f4aa4f0 100644
--- a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
+++ b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
@@ -52,8 +52,8 @@ protected:
void init() override;
MOCK_METHOD(APerformanceHintManager*, fakeGetManager, ());
- MOCK_METHOD(APerformanceHintSession*, fakeCreateSession,
- (APerformanceHintManager*, const int32_t*, size_t, int64_t));
+ MOCK_METHOD(APerformanceHintSession*, fakeCreateSessionInternal,
+ (APerformanceHintManager*, const int32_t*, size_t, int64_t, SessionTag));
MOCK_METHOD(void, fakeCloseSession, (APerformanceHintSession*));
MOCK_METHOD(void, fakeUpdateTargetWorkDuration, (APerformanceHintSession*, int64_t));
MOCK_METHOD(void, fakeReportActualWorkDuration, (APerformanceHintSession*, int64_t));
@@ -72,22 +72,28 @@ protected:
// Must be static so we can point to them as normal fn pointers with HintSessionBinding
static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); };
- static APerformanceHintSession* stubCreateSession(APerformanceHintManager* manager,
- const int32_t* ids, size_t idsSize,
- int64_t initialTarget) {
- return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
+ static APerformanceHintSession* stubCreateSessionInternal(APerformanceHintManager* manager,
+ const int32_t* ids, size_t idsSize,
+ int64_t initialTarget,
+ SessionTag tag) {
+ return sMockBinding->fakeCreateSessionInternal(manager, ids, idsSize, initialTarget,
+ SessionTag::HWUI);
}
- static APerformanceHintSession* stubManagedCreateSession(APerformanceHintManager* manager,
- const int32_t* ids, size_t idsSize,
- int64_t initialTarget) {
+ static APerformanceHintSession* stubManagedCreateSessionInternal(
+ APerformanceHintManager* manager, const int32_t* ids, size_t idsSize,
+ int64_t initialTarget, SessionTag tag) {
sMockBinding->allowCreationToFinish.get_future().wait();
- return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
+ return sMockBinding->fakeCreateSessionInternal(manager, ids, idsSize, initialTarget,
+ SessionTag::HWUI);
}
- static APerformanceHintSession* stubSlowCreateSession(APerformanceHintManager* manager,
- const int32_t* ids, size_t idsSize,
- int64_t initialTarget) {
+ static APerformanceHintSession* stubSlowCreateSessionInternal(APerformanceHintManager* manager,
+ const int32_t* ids,
+ size_t idsSize,
+ int64_t initialTarget,
+ SessionTag tag) {
std::this_thread::sleep_for(50ms);
- return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
+ return sMockBinding->fakeCreateSessionInternal(manager, ids, idsSize, initialTarget,
+ SessionTag::HWUI);
}
static void stubCloseSession(APerformanceHintSession* session) {
sMockBinding->fakeCloseSession(session);
@@ -139,14 +145,14 @@ void HintSessionWrapperTests::SetUp() {
mWrapper = std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId);
mWrapper->mBinding = sMockBinding;
EXPECT_CALL(*sMockBinding, fakeGetManager).WillOnce(Return(managerPtr));
- ON_CALL(*sMockBinding, fakeCreateSession).WillByDefault(Return(sessionPtr));
+ ON_CALL(*sMockBinding, fakeCreateSessionInternal).WillByDefault(Return(sessionPtr));
ON_CALL(*sMockBinding, fakeSetThreads).WillByDefault(Return(0));
}
void HintSessionWrapperTests::MockHintSessionBinding::init() {
sMockBinding->getManager = &stubGetManager;
- if (sMockBinding->createSession == nullptr) {
- sMockBinding->createSession = &stubCreateSession;
+ if (sMockBinding->createSessionInternal == nullptr) {
+ sMockBinding->createSessionInternal = &stubCreateSessionInternal;
}
sMockBinding->closeSession = &stubCloseSession;
sMockBinding->updateTargetWorkDuration = &stubUpdateTargetWorkDuration;
@@ -163,14 +169,14 @@ void HintSessionWrapperTests::TearDown() {
TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) {
EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
- sMockBinding->createSession = stubSlowCreateSession;
+ sMockBinding->createSessionInternal = stubSlowCreateSessionInternal;
mWrapper->init();
mWrapper = nullptr;
Mock::VerifyAndClearExpectations(sMockBinding.get());
}
TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) {
- EXPECT_CALL(*sMockBinding, fakeCreateSession(managerPtr, _, Gt(1), _)).Times(1);
+ EXPECT_CALL(*sMockBinding, fakeCreateSessionInternal(managerPtr, _, Gt(1), _, _)).Times(1);
mWrapper->init();
waitForWrapperReady();
}
@@ -219,7 +225,7 @@ TEST_F(HintSessionWrapperTests, delayedDeletionResolvesBeforeAsyncCreationFinish
// Here we test whether queueing delayedDestroy works while creation is still happening, if
// creation happens after
EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
- sMockBinding->createSession = &stubManagedCreateSession;
+ sMockBinding->createSessionInternal = &stubManagedCreateSessionInternal;
// Start creating the session and destroying it at the same time
mWrapper->init();
@@ -246,7 +252,7 @@ TEST_F(HintSessionWrapperTests, delayedDeletionResolvesAfterAsyncCreationFinishe
// Here we test whether queueing delayedDestroy works while creation is still happening, if
// creation happens before
EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
- sMockBinding->createSession = &stubManagedCreateSession;
+ sMockBinding->createSessionInternal = &stubManagedCreateSessionInternal;
// Start creating the session and destroying it at the same time
mWrapper->init();
@@ -352,7 +358,7 @@ TEST_F(HintSessionWrapperTests, manualSessionDestroyPlaysNiceWithDelayedDestruct
}
TEST_F(HintSessionWrapperTests, setThreadsUpdatesSessionThreads) {
- EXPECT_CALL(*sMockBinding, fakeCreateSession(managerPtr, _, Gt(1), _)).Times(1);
+ EXPECT_CALL(*sMockBinding, fakeCreateSessionInternal(managerPtr, _, Gt(1), _, _)).Times(1);
EXPECT_CALL(*sMockBinding, fakeSetThreads(sessionPtr, testing::IsSupersetOf({11, 22})))
.Times(1);
mWrapper->init();
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index f6c57927cc85..6a560b365247 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -403,7 +403,7 @@ skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) {
}
static skcms_TransferFunction trfn_apply_gain(const skcms_TransferFunction trfn, float gain) {
- float pow_gain_ginv = sk_float_pow(gain, 1 / trfn.g);
+ float pow_gain_ginv = std::pow(gain, 1 / trfn.g);
skcms_TransferFunction result;
result.g = trfn.g;
result.a = trfn.a * pow_gain_ginv;