diff options
Diffstat (limited to 'libs')
139 files changed, 2147 insertions, 577 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/layout/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/layout/CommonFoldingFeature.java index 85c4fe1193ee..252974dd474e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/layout/CommonFoldingFeature.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/layout/CommonFoldingFeature.java @@ -25,6 +25,7 @@ import android.hardware.devicestate.DeviceStateManager; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -212,7 +213,8 @@ public final class CommonFoldingFeature { @NonNull private final Rect mRect; - CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) { + @VisibleForTesting + public CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) { assertReportableState(state); this.mType = type; this.mState = state; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 2ab0310d6789..fcf3a3759f7a 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -57,6 +57,8 @@ class WindowExtensionsImpl implements WindowExtensions { */ private static final int NO_LEVEL_OVERRIDE = -1; + private static final int EXTENSIONS_VERSION_V9 = 9; + private static final int EXTENSIONS_VERSION_V8 = 8; private static final int EXTENSIONS_VERSION_V7 = 7; @@ -80,6 +82,9 @@ class WindowExtensionsImpl implements WindowExtensions { */ @VisibleForTesting static int getExtensionsVersionCurrentPlatform() { + if (Flags.wlinfoOncreate()) { + return EXTENSIONS_VERSION_V9; + } if (Flags.aeBackStackRestore()) { return EXTENSIONS_VERSION_V8; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java index a3d2d7f4dcdf..438532725686 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java @@ -16,6 +16,10 @@ package androidx.window.extensions.area; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER; import android.app.Activity; @@ -23,6 +27,7 @@ import android.content.Context; import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateRequest; +import android.hardware.devicestate.feature.flags.Flags; import android.hardware.display.DisplayManager; import android.util.ArraySet; import android.util.DisplayMetrics; @@ -72,18 +77,18 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @GuardedBy("mLock") private final ArraySet<Consumer<ExtensionWindowAreaStatus>> mRearDisplayPresentationStatusListeners = new ArraySet<>(); - private final int mRearDisplayState; + private int mRearDisplayState = INVALID_DEVICE_STATE_IDENTIFIER; private final int mConcurrentDisplayState; @NonNull - private final int[] mFoldedDeviceStates; + private int[] mFoldedDeviceStates = new int[0]; private long mRearDisplayAddress = INVALID_DISPLAY_ADDRESS; @WindowAreaSessionState private int mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE; @GuardedBy("mLock") - private int mCurrentDeviceState = INVALID_DEVICE_STATE_IDENTIFIER; + private DeviceState mCurrentDeviceState = INVALID_DEVICE_STATE; @GuardedBy("mLock") - private int[] mCurrentSupportedDeviceStates; + private List<DeviceState> mCurrentSupportedDeviceStates; @GuardedBy("mLock") private DeviceStateRequest mRearDisplayStateRequest; @@ -103,16 +108,25 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, mDisplayManager = context.getSystemService(DisplayManager.class); mExecutor = context.getMainExecutor(); - // TODO(b/329436166): Update the usage of device state manager API's - mCurrentSupportedDeviceStates = getSupportedStateIdentifiers( - mDeviceStateManager.getSupportedDeviceStates()); - mFoldedDeviceStates = context.getResources().getIntArray( - R.array.config_foldedDeviceStates); + mCurrentSupportedDeviceStates = mDeviceStateManager.getSupportedDeviceStates(); - // TODO(b/236022708) Move rear display state to device state config file - mRearDisplayState = context.getResources().getInteger( - R.integer.config_deviceStateRearDisplay); + if (Flags.deviceStatePropertyMigration()) { + for (int i = 0; i < mCurrentSupportedDeviceStates.size(); i++) { + DeviceState state = mCurrentSupportedDeviceStates.get(i); + if (state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY)) { + mRearDisplayState = state.getIdentifier(); + break; + } + } + } else { + mFoldedDeviceStates = context.getResources().getIntArray( + R.array.config_foldedDeviceStates); + // TODO(b/236022708) Move rear display state to device state config file + mRearDisplayState = context.getResources().getInteger( + R.integer.config_deviceStateRearDisplay); + } + // TODO(b/374351956) Use DeviceState API when the dual display state is always returned mConcurrentDisplayState = context.getResources().getInteger( R.integer.config_deviceStateConcurrentRearDisplay); @@ -147,7 +161,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, mRearDisplayStatusListeners.add(consumer); // If current device state is still invalid, the initial value has not been provided. - if (mCurrentDeviceState == INVALID_DEVICE_STATE_IDENTIFIER) { + if (mCurrentDeviceState.getIdentifier() == INVALID_DEVICE_STATE_IDENTIFIER) { return; } consumer.accept(getCurrentRearDisplayModeStatus()); @@ -312,7 +326,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, mRearDisplayPresentationStatusListeners.add(consumer); // If current device state is still invalid, the initial value has not been provided - if (mCurrentDeviceState == INVALID_DEVICE_STATE_IDENTIFIER) { + if (mCurrentDeviceState.getIdentifier() == INVALID_DEVICE_STATE_IDENTIFIER) { return; } @WindowAreaStatus int currentStatus = getCurrentRearDisplayPresentationModeStatus(); @@ -452,8 +466,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @Override public void onSupportedStatesChanged(@NonNull List<DeviceState> supportedStates) { synchronized (mLock) { - // TODO(b/329436166): Update the usage of device state manager API's - mCurrentSupportedDeviceStates = getSupportedStateIdentifiers(supportedStates); + mCurrentSupportedDeviceStates = supportedStates; updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus()); updateRearDisplayPresentationStatusListeners( getCurrentRearDisplayPresentationModeStatus()); @@ -463,8 +476,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @Override public void onDeviceStateChanged(@NonNull DeviceState state) { synchronized (mLock) { - // TODO(b/329436166): Update the usage of device state manager API's - mCurrentDeviceState = state.getIdentifier(); + mCurrentDeviceState = state; updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus()); updateRearDisplayPresentationStatusListeners( getCurrentRearDisplayPresentationModeStatus()); @@ -477,7 +489,8 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, return WindowAreaComponent.STATUS_UNSUPPORTED; } - if (!ArrayUtils.contains(mCurrentSupportedDeviceStates, mRearDisplayState)) { + if (!deviceStateListContainsIdentifier(mCurrentSupportedDeviceStates, + mRearDisplayState)) { return WindowAreaComponent.STATUS_UNAVAILABLE; } @@ -488,15 +501,6 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, return WindowAreaComponent.STATUS_AVAILABLE; } - // TODO(b/329436166): Remove and update the usage of device state manager API's - private int[] getSupportedStateIdentifiers(@NonNull List<DeviceState> states) { - int[] identifiers = new int[states.size()]; - for (int i = 0; i < states.size(); i++) { - identifiers[i] = states.get(i).getIdentifier(); - } - return identifiers; - } - /** * Helper method to determine if a rear display session is currently active by checking * if the current device state is that which corresponds to {@code mRearDisplayState}. @@ -505,7 +509,31 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, */ @GuardedBy("mLock") private boolean isRearDisplayActive() { - return mCurrentDeviceState == mRearDisplayState; + if (Flags.deviceStatePropertyApi()) { + return mCurrentDeviceState.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY); + } else { + return mCurrentDeviceState.getIdentifier() == mRearDisplayState; + } + } + + @GuardedBy("mLock") + private boolean isRearDisplayPresentationModeActive() { + if (Flags.deviceStatePropertyApi()) { + return mCurrentDeviceState.hasProperty(PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT); + } else { + return mCurrentDeviceState.getIdentifier() == mConcurrentDisplayState; + } + } + + @GuardedBy("mLock") + private boolean deviceStateListContainsIdentifier(List<DeviceState> deviceStates, + int identifier) { + for (int i = 0; i < deviceStates.size(); i++) { + if (deviceStates.get(i).getIdentifier() == identifier) { + return true; + } + } + return false; } @GuardedBy("mLock") @@ -526,12 +554,12 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, return WindowAreaComponent.STATUS_UNSUPPORTED; } - if (mCurrentDeviceState == mConcurrentDisplayState) { + if (isRearDisplayPresentationModeActive()) { return WindowAreaComponent.STATUS_ACTIVE; } - if (!ArrayUtils.contains(mCurrentSupportedDeviceStates, mConcurrentDisplayState) - || isDeviceFolded()) { + if (!deviceStateListContainsIdentifier(mCurrentSupportedDeviceStates, + mConcurrentDisplayState) || isDeviceFolded()) { return WindowAreaComponent.STATUS_UNAVAILABLE; } return WindowAreaComponent.STATUS_AVAILABLE; @@ -539,7 +567,12 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @GuardedBy("mLock") private boolean isDeviceFolded() { - return ArrayUtils.contains(mFoldedDeviceStates, mCurrentDeviceState); + if (Flags.deviceStatePropertyApi()) { + return mCurrentDeviceState.hasProperty( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY); + } else { + return ArrayUtils.contains(mFoldedDeviceStates, mCurrentDeviceState.getIdentifier()); + } } @GuardedBy("mLock") 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 60e1a506ab73..4c7e47769613 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -2314,15 +2314,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @GuardedBy("mLock") @Nullable Bundle getPlaceholderOptions(@NonNull Activity primaryActivity, boolean isOnCreated) { - // Setting avoid move to front will also skip the animation. We only want to do that when - // the Task is currently in background. // Check if the primary is resumed or if this is called when the primary is onCreated // (not resumed yet). if (isOnCreated || primaryActivity.isResumed()) { // Only set trigger type if the launch happens in foreground. mTransactionManager.getCurrentTransactionRecord() .setOriginType(TASK_FRAGMENT_TRANSIT_OPEN); - return null; } final ActivityOptions options = ActivityOptions.makeBasic(); options.setAvoidMoveToFront(); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index f1ea19a60f97..556da3798df5 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -74,6 +74,12 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { @GuardedBy("mLock") private final DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer; + /** + * The last reported folding features from the device. This is initialized in the constructor + * because the data change callback added to {@link #mFoldingFeatureProducer} is immediately + * called. This is due to current device state from the device state manager already being + * available in the {@link DeviceStateManagerFoldingFeatureProducer}. + */ @GuardedBy("mLock") private final List<CommonFoldingFeature> mLastReportedFoldingFeatures = new ArrayList<>(); @@ -308,6 +314,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { * @param context a proxy for the {@link android.view.Window} that contains the * {@link DisplayFeature}. */ + @NonNull private WindowLayoutInfo getWindowLayoutInfo(@NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) { List<DisplayFeature> displayFeatureList = getDisplayFeatures(context, storedFeatures); @@ -329,6 +336,14 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } } + @Override + @NonNull + public WindowLayoutInfo getCurrentWindowLayoutInfo(@NonNull @UiContext Context context) { + synchronized (mLock) { + return getWindowLayoutInfo(context, mLastReportedFoldingFeatures); + } + } + /** * Returns the {@link SupportedWindowFeatures} for the device. This list does not change over * time. 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 05124121fe7b..b8a36eb4d3f5 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 @@ -166,6 +166,8 @@ public class SplitControllerTest { private List<SplitInfo> mSplitInfos; private TransactionManager mTransactionManager; private ActivityThread mCurrentActivityThread; + private final ArgumentCaptor<Bundle> mBundleArgumentCaptor = + ArgumentCaptor.forClass(Bundle.class); @Before public void setUp() { @@ -685,9 +687,13 @@ public class SplitControllerTest { false /* isOnReparent */); assertTrue(result); - verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT, - mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */), - placeholderRule, SPLIT_ATTRIBUTES, true /* isPlaceholder */); + verify(mSplitPresenter).startActivityToSide(eq(mTransaction), eq(mActivity), + eq(PLACEHOLDER_INTENT), mBundleArgumentCaptor.capture(), + eq(placeholderRule), eq(SPLIT_ATTRIBUTES), eq(true) /* isPlaceholder */); + + final ActivityOptions activityOptions = + new ActivityOptions(mBundleArgumentCaptor.getValue()); + assertTrue(activityOptions.getAvoidMoveToFront()); } @Test @@ -720,9 +726,13 @@ public class SplitControllerTest { false /* isOnReparent */); assertTrue(result); - verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT, - mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */), - placeholderRule, SPLIT_ATTRIBUTES, true /* isPlaceholder */); + verify(mSplitPresenter).startActivityToSide(eq(mTransaction), eq(mActivity), + eq(PLACEHOLDER_INTENT), mBundleArgumentCaptor.capture(), + eq(placeholderRule), eq(SPLIT_ATTRIBUTES), eq(true) /* isPlaceholder */); + + final ActivityOptions activityOptions = + new ActivityOptions(mBundleArgumentCaptor.getValue()); + assertTrue(activityOptions.getAvoidMoveToFront()); } @Test @@ -755,9 +765,13 @@ public class SplitControllerTest { false /* isOnReparent */); assertTrue(result); - verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT, - mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */), - placeholderRule, SPLIT_ATTRIBUTES, true /* isPlaceholder */); + verify(mSplitPresenter).startActivityToSide(eq(mTransaction), eq(mActivity), + eq(PLACEHOLDER_INTENT), mBundleArgumentCaptor.capture(), + eq(placeholderRule), eq(SPLIT_ATTRIBUTES), eq(true) /* isPlaceholder */); + + final ActivityOptions activityOptions = + new ActivityOptions(mBundleArgumentCaptor.getValue()); + assertTrue(activityOptions.getAvoidMoveToFront()); } @Test @@ -1065,16 +1079,8 @@ public class SplitControllerTest { public void testGetPlaceholderOptions() { // Setup to make sure a transaction record is started. mTransactionManager.startNewTransaction(); - doReturn(true).when(mActivity).isResumed(); - - assertNull(mSplitController.getPlaceholderOptions(mActivity, false /* isOnCreated */)); - - doReturn(false).when(mActivity).isResumed(); - - assertNull(mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */)); - // Launch placeholder without moving the Task to front if the Task is now in background (not - // resumed or onCreated). + // Launch placeholder without moving the Task to front final Bundle options = mSplitController.getPlaceholderOptions(mActivity, false /* isOnCreated */); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java index ff0a82fe05d6..ed4eddf7c209 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java @@ -16,22 +16,30 @@ package androidx.window.extensions.layout; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.mock; +import android.app.WindowConfiguration; import android.content.Context; import android.content.ContextWrapper; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; +import android.view.Display; +import androidx.annotation.NonNull; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; +import androidx.window.common.layout.CommonFoldingFeature; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Collections; +import java.util.List; /** * Test class for {@link WindowLayoutComponentImpl}. @@ -44,31 +52,91 @@ import java.util.Collections; @RunWith(AndroidJUnit4.class) public class WindowLayoutComponentImplTest { + private final Context mAppContext = ApplicationProvider.getApplicationContext(); + + @NonNull private WindowLayoutComponentImpl mWindowLayoutComponent; @Before public void setUp() { - mWindowLayoutComponent = new WindowLayoutComponentImpl( - ApplicationProvider.getApplicationContext(), + mWindowLayoutComponent = new WindowLayoutComponentImpl(mAppContext, mock(DeviceStateManagerFoldingFeatureProducer.class)); } @Test - public void testAddWindowLayoutListenerOnFakeUiContext_noCrash() { - final Context fakeUiContext = createTestContext(); + public void testAddWindowLayoutListener_onFakeUiContext_noCrash() { + final Context fakeUiContext = new FakeUiContext(mAppContext); mWindowLayoutComponent.addWindowLayoutInfoListener(fakeUiContext, info -> {}); mWindowLayoutComponent.onDisplayFeaturesChanged(Collections.emptyList()); } - private static Context createTestContext() { - return new FakeUiContext(ApplicationProvider.getApplicationContext()); + @Test + public void testGetCurrentWindowLayoutInfo_noFoldingFeature_returnsEmptyList() { + final Context testUiContext = new TestUiContext(mAppContext); + + final WindowLayoutInfo layoutInfo = + mWindowLayoutComponent.getCurrentWindowLayoutInfo(testUiContext); + + assertThat(layoutInfo.getDisplayFeatures()).isEmpty(); + } + + @Test + public void testGetCurrentWindowLayoutInfo_hasFoldingFeature_returnsWindowLayoutInfo() { + final Context testUiContext = new TestUiContext(mAppContext); + final WindowConfiguration windowConfiguration = + testUiContext.getResources().getConfiguration().windowConfiguration; + final Rect featureRect = windowConfiguration.getBounds(); + final CommonFoldingFeature foldingFeature = new CommonFoldingFeature( + CommonFoldingFeature.COMMON_TYPE_HINGE, + CommonFoldingFeature.COMMON_STATE_FLAT, + featureRect + ); + mWindowLayoutComponent.onDisplayFeaturesChanged(List.of(foldingFeature)); + + final WindowLayoutInfo layoutInfo = + mWindowLayoutComponent.getCurrentWindowLayoutInfo(testUiContext); + + assertThat(layoutInfo.getDisplayFeatures()).containsExactly(new FoldingFeature( + featureRect, FoldingFeature.TYPE_HINGE, FoldingFeature.STATE_FLAT)); + } + + @Test + public void testGetCurrentWindowLayoutInfo_nonUiContext_returnsEmptyList() { + final WindowLayoutInfo layoutInfo = + mWindowLayoutComponent.getCurrentWindowLayoutInfo(mAppContext); + + assertThat(layoutInfo.getDisplayFeatures()).isEmpty(); + } + + /** + * A {@link Context} that simulates a UI context specifically for testing purposes. + * This class overrides {@link Context#getAssociatedDisplayId()} to return + * {@link Display#DEFAULT_DISPLAY}, ensuring the context is tied to the default display, + * and {@link Context#isUiContext()} to always return {@code true}, simulating a UI context. + */ + private static class TestUiContext extends ContextWrapper { + + TestUiContext(Context base) { + super(base); + } + + @Override + public int getAssociatedDisplayId() { + return Display.DEFAULT_DISPLAY; + } + + @Override + public boolean isUiContext() { + return true; + } } /** - * A {@link android.content.Context} overrides {@link android.content.Context#isUiContext} to - * {@code true}. + * A {@link Context} that cheats by overriding {@link Context#isUiContext} to always + * return {@code true}. This is useful for scenarios where a UI context is needed, + * but the underlying context isn't actually a UI one. */ private static class FakeUiContext extends ContextWrapper { diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 4642fe59bcb2..42188dec4236 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -57,7 +57,7 @@ filegroup { path: "src", } -genrule { +java_genrule { name: "wm_shell_protolog_src", srcs: [ ":protolog-impl", @@ -77,7 +77,7 @@ genrule { out: ["wm_shell_protolog.srcjar"], } -genrule { +java_genrule { name: "generate-wm_shell_protolog.json", srcs: [ ":wm_shell_protolog-groups", @@ -94,7 +94,7 @@ genrule { out: ["wm_shell_protolog.json"], } -genrule { +java_genrule { name: "gen-wmshell.protolog.pb", srcs: [ ":wm_shell_protolog-groups", @@ -111,7 +111,7 @@ genrule { out: ["wmshell.protolog.pb"], } -genrule { +java_genrule { name: "protolog.json.gz", srcs: [":generate-wm_shell_protolog.json"], out: ["wmshell.protolog.json.gz"], @@ -157,6 +157,13 @@ java_library { } filegroup { + name: "wm_shell-shared-utils", + srcs: [ + "shared/src/com/android/wm/shell/shared/TransitionUtil.java", + ], +} + +filegroup { name: "wm_shell-shared-aidls", srcs: [ diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig index cf0a975b6c30..714d5e0de367 100644 --- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -168,3 +168,13 @@ flag { description: "Enables flexibile split feature for split screen" bug: "349828130" } + +flag { + name: "enable_task_view_controller_cleanup" + namespace: "multitasking" + description: "Fix memory leak with task view controllers" + bug: "369995920" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png Binary files differindex c7b4c65b8c4b..736bca7f5a7c 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png Binary files differindex c7b4c65b8c4b..736bca7f5a7c 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png Binary files differindex c72944222e66..e540b455028b 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png Binary files differindex c72944222e66..e540b455028b 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml index 806d026a7e7c..4a42616a45ec 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml @@ -16,40 +16,42 @@ --> <com.android.wm.shell.shared.bubbles.BubblePopupView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" - android:layout_marginHorizontal="@dimen/bubble_popup_margin_horizontal" android:layout_marginTop="@dimen/bubble_popup_margin_top" - android:elevation="@dimen/bubble_manage_menu_elevation" + android:layout_marginHorizontal="@dimen/bubble_popup_margin_horizontal" + android:layout_marginBottom="@dimen/bubble_popup_margin_bottom" + android:elevation="@dimen/bubble_popup_elevation" android:gravity="center_horizontal" android:orientation="vertical"> <ImageView - android:layout_width="32dp" - android:layout_height="32dp" - android:tint="?android:attr/colorAccent" + android:layout_width="@dimen/bubble_popup_icon_size" + android:layout_height="@dimen/bubble_popup_icon_size" + android:tint="?androidprv:attr/materialColorPrimary" android:contentDescription="@null" android:src="@drawable/pip_ic_settings"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" + android:layout_marginTop="@dimen/bubble_popup_text_margin" android:maxWidth="@dimen/bubble_popup_content_max_width" android:maxLines="1" android:ellipsize="end" android:textAppearance="@android:style/TextAppearance.DeviceDefault.Headline" - android:textColor="?android:attr/textColorPrimary" + android:textColor="?androidprv:attr/materialColorOnSurface" android:text="@string/bubble_bar_education_manage_title"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" + android:layout_marginTop="@dimen/bubble_popup_text_margin" android:maxWidth="@dimen/bubble_popup_content_max_width" android:textAppearance="@android:style/TextAppearance.DeviceDefault" - android:textColor="?android:attr/textColorSecondary" + android:textColor="?androidprv:attr/materialColorOnSurfaceVariant" android:textAlignment="center" android:text="@string/bubble_bar_education_manage_text"/> diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml index 7fa586c626be..f19c3c762d9d 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml @@ -16,40 +16,42 @@ --> <com.android.wm.shell.shared.bubbles.BubblePopupView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" - android:layout_margin="@dimen/bubble_popup_margin_horizontal" - android:layout_marginBottom="120dp" - android:elevation="@dimen/bubble_manage_menu_elevation" + android:layout_marginTop="@dimen/bubble_popup_margin_top" + android:layout_marginHorizontal="@dimen/bubble_popup_margin_horizontal" + android:layout_marginBottom="@dimen/bubble_popup_margin_bottom" + android:elevation="@dimen/bubble_popup_elevation" android:gravity="center_horizontal" android:orientation="vertical"> <ImageView - android:layout_width="32dp" - android:layout_height="32dp" - android:tint="?android:attr/colorAccent" + android:layout_width="@dimen/bubble_popup_icon_size" + android:layout_height="@dimen/bubble_popup_icon_size" + android:tint="?androidprv:attr/materialColorOutline" android:contentDescription="@null" android:src="@drawable/ic_floating_landscape"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" + android:layout_marginTop="@dimen/bubble_popup_text_margin" android:maxWidth="@dimen/bubble_popup_content_max_width" android:maxLines="1" android:ellipsize="end" android:textAppearance="@android:style/TextAppearance.DeviceDefault.Headline" - android:textColor="?android:attr/textColorPrimary" + android:textColor="?androidprv:attr/materialColorOnSurface" android:text="@string/bubble_bar_education_stack_title"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" + android:layout_marginTop="@dimen/bubble_popup_text_margin" android:maxWidth="@dimen/bubble_popup_content_max_width" android:textAppearance="@android:style/TextAppearance.DeviceDefault" - android:textColor="?android:attr/textColorSecondary" + android:textColor="?androidprv:attr/materialColorOnSurfaceVariant" android:textAlignment="center" android:text="@string/bubble_bar_education_stack_text"/> diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml index a0718d9ba148..b29e8bf5e922 100644 --- a/libs/WindowManager/Shell/res/values-af/strings.xml +++ b/libs/WindowManager/Shell/res/values-af/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gryp skerm vas"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App kan nie hierheen geskuif word nie"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeer"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Stel terug"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Spring na links"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Spring na regs"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Maak By Verstek Oop-instellings"</string> diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml index f160c70b7505..d96033f286f8 100644 --- a/libs/WindowManager/Shell/res/values-am/strings.xml +++ b/libs/WindowManager/Shell/res/values-am/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ማያ ገጹን አሳድግ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"መተግበሪያ ወደዚህ መንቀሳቀስ አይችልም"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"አሳድግ"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ወደነበረበት መልስ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ወደ ግራ አሳድግ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ወደ ቀኝ አሳድግ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"በነባሪ ቅንብሮች ክፈት"</string> diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index 81706a21f77f..42ef4c307b1e 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"التقاط صورة للشاشة"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"لا يمكن نقل التطبيق إلى هنا"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"تكبير"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"استعادة"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"المحاذاة إلى اليسار"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"المحاذاة إلى اليمين"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"إعدادات الفتح تلقائيًا"</string> diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml index 9fd4941afe84..8e9c7045118f 100644 --- a/libs/WindowManager/Shell/res/values-as/strings.xml +++ b/libs/WindowManager/Shell/res/values-as/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্ৰীন স্নেপ কৰক"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ইয়ালৈ এপ্টো আনিব নোৱাৰি"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"মেক্সিমাইজ কৰক"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাওঁফাললৈ স্নেপ কৰক"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"সোঁফাললৈ স্নেপ কৰক"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফ’ল্ট ছেটিং খোলক"</string> diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml index 3ab639730a90..1310f6f743f4 100644 --- a/libs/WindowManager/Shell/res/values-az/strings.xml +++ b/libs/WindowManager/Shell/res/values-az/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranı çəkin"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Tətbiqi bura köçürmək mümkün deyil"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Böyüdün"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Bərpa edin"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tərəf çəkin"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Sağa tərəf çəkin"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Defolt ayarlarla açın"</string> diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml index 507625661527..b9c42397f8c7 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Uklopi ekran"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija ne može da se premesti ovde"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Uvećajte"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vratite"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prikačite levo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Prikačite desno"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Podešavanje Podrazumevano otvaraj"</string> diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml index 95530c44562e..5d389d849433 100644 --- a/libs/WindowManager/Shell/res/values-be/strings.xml +++ b/libs/WindowManager/Shell/res/values-be/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Размясціць на палавіне экрана"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Нельга перамясціць сюды праграму"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Разгарнуць"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Аднавіць"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Размясціць злева"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Размясціць справа"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Налады параметра \"Адкрываць стандартна\""</string> diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml index db498c1dfe1a..807878620d90 100644 --- a/libs/WindowManager/Shell/res/values-bg/strings.xml +++ b/libs/WindowManager/Shell/res/values-bg/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Прилепване на екрана"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложението не може да бъде преместено тук"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увеличаване"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Възстановяване"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прилепване наляво"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Прилепване надясно"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Отваряне на настройките по подразбиране"</string> diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml index 4c2025378f04..8db7144e6db6 100644 --- a/libs/WindowManager/Shell/res/values-bn/strings.xml +++ b/libs/WindowManager/Shell/res/values-bn/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্রিনে অ্যাপ মানানসই হিসেবে ছোট বড় করুন"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"অ্যাপটি এখানে সরানো যাবে না"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"বড় করুন"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাঁদিকে স্ন্যাপ করুন"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ডানদিকে স্ন্যাপ করুন"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফল্ট হিসেবে থাকা সেটিংস খুলুন"</string> diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml index 102a91233627..3d922d8334c8 100644 --- a/libs/WindowManager/Shell/res/values-bs/strings.xml +++ b/libs/WindowManager/Shell/res/values-bs/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snimi ekran"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ne možete premjestiti aplikaciju ovdje"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziranje"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vraćanje"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pomicanje ulijevo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pomicanje udesno"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvaranje prema zadanim postavkama"</string> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index 3e3fcd05a05b..dc96cd0f72ec 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajusta la pantalla"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"L\'aplicació no es pot moure aquí"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximitza"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaura"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajusta a l\'esquerra"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajusta a la dreta"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configuració d\'obertura predeterminada"</string> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index 0627f54ecf47..30ba78ad8e06 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Rozpůlit obrazovku"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikaci sem nelze přesunout"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovat"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnovit"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Přichytit vlevo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Přichytit vpravo"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otevírat podle výchozího nastavení"</string> diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml index ed9e5f07bad3..433b858cfd84 100644 --- a/libs/WindowManager/Shell/res/values-da/strings.xml +++ b/libs/WindowManager/Shell/res/values-da/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tilpas skærm"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apps kan ikke flyttes hertil"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimér"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Gendan"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fastgør til venstre"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fastgør til højre"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Indstillinger for automatisk åbning"</string> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index ec1cb03d7108..0b4d189716c2 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Bildschirm teilen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Die App kann nicht hierher verschoben werden"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximieren"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links andocken"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Rechts andocken"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Einstellungen für die Option „Standardmäßig öffnen“"</string> diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml index 7a690ce2e188..beff019e6897 100644 --- a/libs/WindowManager/Shell/res/values-el/strings.xml +++ b/libs/WindowManager/Shell/res/values-el/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Προβολή στο μισό της οθόνης"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Δεν είναι δυνατή η μετακίνηση της εφαρμογής εδώ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Μεγιστοποίηση"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Επαναφορά"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Κούμπωμα αριστερά"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Κούμπωμα δεξιά"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Άνοιγμα ρυθμίσεων από προεπιλογή"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index afb3f8e1a5ce..01d3d25e7e4f 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml index aa392519145b..20ec076a33d1 100644 --- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap Screen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximize"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index afb3f8e1a5ce..01d3d25e7e4f 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index afb3f8e1a5ce..01d3d25e7e4f 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml index 5244a61883d0..c9e7ecbb056e 100644 --- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml +++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"No se puede mover la app aquí"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restablecer"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar a la izquierda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar a la derecha"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir con la configuración predeterminada"</string> diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml index a673351f8316..903294bbd1cf 100644 --- a/libs/WindowManager/Shell/res/values-es/strings.xml +++ b/libs/WindowManager/Shell/res/values-es/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"La aplicación no se puede mover aquí"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Acoplar a la izquierda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Acoplar a la derecha"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir con los ajustes predeterminados"</string> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index 686385c4bdb8..f6bebff9a4f3 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Kuva poolel ekraanil"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Rakendust ei saa siia teisaldada"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeeri"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Taasta"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Tõmmake vasakule"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Tõmmake paremale"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Avamisviisi vaikeseaded"</string> diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml index 1f0e54cedd58..267452f2e6e1 100644 --- a/libs/WindowManager/Shell/res/values-eu/strings.xml +++ b/libs/WindowManager/Shell/res/values-eu/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zatitu pantaila"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikazioa ezin da hona ekarri"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizatu"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Leheneratu"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ezarri ezkerrean"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ezarri eskuinean"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Modu lehenetsian irekitzearen ezarpenak"</string> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index ad39b28eed1e..ec4779b0a129 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"بزرگ کردن صفحه"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"برنامه را نمیتوان به اینجا منتقل کرد"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بزرگ کردن"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"بازیابی کردن"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"کشیدن بهچپ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"کشیدن بهراست"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"تنظیمات باز کردن بهطور پیشفرض"</string> diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml index 2f0edd33226b..6dcd20cc0227 100644 --- a/libs/WindowManager/Shell/res/values-fi/strings.xml +++ b/libs/WindowManager/Shell/res/values-fi/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Jaa näyttö"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Sovellusta ei voi siirtää tänne"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Suurenna"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Palauta"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Siirrä vasemmalle"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Siirrä oikealle"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Avaa oletusasetusten mukaan"</string> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index 245396156aad..df6d503c2121 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aligner l\'écran"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurer"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Épingler à gauche"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Épingler à droite"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ouvrir les paramètres par défaut"</string> diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml index aee234565ad0..3a7f2f0bd4fa 100644 --- a/libs/WindowManager/Shell/res/values-fr/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fractionner l\'écran"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurer"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ancrer à gauche"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ancrer à droite"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ouvrir les paramètres par défaut"</string> diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml index b61588ac24ed..cb7bb324de27 100644 --- a/libs/WindowManager/Shell/res/values-gl/strings.xml +++ b/libs/WindowManager/Shell/res/values-gl/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar pantalla"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Non se pode mover aquí a aplicación"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Axustar á esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Axustar á dereita"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir coa configuración predeterminada"</string> diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml index fd4f2baeb871..bee06fed5fd1 100644 --- a/libs/WindowManager/Shell/res/values-gu/strings.xml +++ b/libs/WindowManager/Shell/res/values-gu/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"સ્ક્રીન સ્નૅપ કરો"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ઍપ અહીં ખસેડી શકાતી નથી"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"મોટું કરો"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ડાબે સ્નૅપ કરો"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"જમણે સ્નૅપ કરો"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"\'ડિફૉલ્ટ તરીકે ખોલો\' સેટિંગ"</string> diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index d2cd23df8296..2c141996b109 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्नैप स्क्रीन"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ऐप्लिकेशन को यहां मूव नहीं किया जा सकता"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"बड़ा करें"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"पहले जैसा करें"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बाईं ओर स्नैप करें"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"दाईं ओर स्नैप करें"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"डिफ़ॉल्ट सेटिंग के हिसाब से खोलें"</string> diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml index 80949b4c8edd..a2a52d13b463 100644 --- a/libs/WindowManager/Shell/res/values-hr/strings.xml +++ b/libs/WindowManager/Shell/res/values-hr/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Izradi snimku zaslona"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija se ne može premjestiti ovdje"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziraj"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vrati"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Poravnaj lijevo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Poravnaj desno"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvori prema zadanim postavkama"</string> diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml index cebf5857db34..03cc9f569d29 100644 --- a/libs/WindowManager/Shell/res/values-hu/strings.xml +++ b/libs/WindowManager/Shell/res/values-hu/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Igazodás a képernyő adott részéhez"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Az alkalmazás nem helyezhető át ide"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Teljes méret"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Visszaállítás"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Balra igazítás"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Jobbra igazítás"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Alapértelmezett beállítások megnyitása"</string> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index 63a9d6d92134..28c762eb0752 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ծալել էկրանը"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Հավելվածը հնարավոր չէ տեղափոխել այստեղ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ծավալել"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Վերականգնել"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ամրացնել ձախ կողմում"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ամրացնել աջ կողմում"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Բացել կարգավորումներն ըստ կանխադրման"</string> diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml index a06d01cc524a..4929b79875a0 100644 --- a/libs/WindowManager/Shell/res/values-in/strings.xml +++ b/libs/WindowManager/Shell/res/values-in/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gabungkan Layar"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikasi tidak dapat dipindahkan ke sini"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimalkan"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Pulihkan"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Maksimalkan ke kiri"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Maksimalkan ke kanan"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buka dengan setelan default"</string> diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml index a20f4604ff90..d7ba53f73926 100644 --- a/libs/WindowManager/Shell/res/values-is/strings.xml +++ b/libs/WindowManager/Shell/res/values-is/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Smelluskjár"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ekki er hægt að færa forritið hingað"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Stækka"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Endurheimta"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Smella til vinstri"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Smella til hægri"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Stillingar sjálfvirkrar opnunar"</string> diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml index 39fd6ba326a6..4522d37d289a 100644 --- a/libs/WindowManager/Shell/res/values-it/strings.xml +++ b/libs/WindowManager/Shell/res/values-it/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aggancia schermo"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossibile spostare l\'app qui"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ingrandisci"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Ripristina"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Aggancia a sinistra"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Aggancia a destra"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Apri in base alle impostazioni predefinite"</string> diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml index 0586d1f8cd16..110d357d0cb2 100644 --- a/libs/WindowManager/Shell/res/values-iw/strings.xml +++ b/libs/WindowManager/Shell/res/values-iw/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"כיווץ המסך"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"לא ניתן להעביר את האפליקציה לכאן"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"הגדלה"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"שחזור"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"הצמדה לשמאל"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"הצמדה לימין"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"הגדרות לפתיחה כברירת מחדל"</string> diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index 8aa8269af8a5..69421da5b450 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"画面のスナップ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"アプリはここに移動できません"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"復元"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"左にスナップ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"右にスナップ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"デフォルトの設定で開く"</string> diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml index 6672599d2763..46859889304a 100644 --- a/libs/WindowManager/Shell/res/values-ka/strings.xml +++ b/libs/WindowManager/Shell/res/values-ka/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"აპლიკაციის დაპატარავება ეკრანზე"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"აპის აქ გადატანა შეუძლებელია"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"მაქსიმალურად გაშლა"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"აღდგენა"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"მარცხნივ გადატანა"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"მარჯვნივ გადატანა"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"პარამეტრების ნაგულისხმევად გახსნა"</string> diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml index 56ae4416192a..84e7ea5b7af3 100644 --- a/libs/WindowManager/Shell/res/values-kk/strings.xml +++ b/libs/WindowManager/Shell/res/values-kk/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды бөлу"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Қолданба бұл жерге қойылмайды."</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Жаю"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Қалпына келтіру"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солға тіркеу"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Оңға тіркеу"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Әдепкісінше ашу параметрлері"</string> diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml index 460b8678c2c9..7eb81e158513 100644 --- a/libs/WindowManager/Shell/res/values-km/strings.xml +++ b/libs/WindowManager/Shell/res/values-km/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ថតអេក្រង់"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"មិនអាចផ្លាស់ទីកម្មវិធីមកទីនេះបានទេ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ពង្រីក"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ស្ដារ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ផ្លាស់ទីទៅឆ្វេង"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ផ្លាស់ទីទៅស្ដាំ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ការកំណត់បើកតាមលំនាំដើម"</string> diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml index 2e2be46c21f6..2b43f573e1ab 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ಸ್ನ್ಯಾಪ್ ಸ್ಕ್ರೀನ್"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ಆ್ಯಪ್ ಅನ್ನು ಇಲ್ಲಿಗೆ ಸರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ಮರುಸ್ಥಾಪಿಸಿ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ಎಡಕ್ಕೆ ಸ್ನ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ಬಲಕ್ಕೆ ಸ್ನ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ಡೀಫಾಲ್ಟ್ ಸೆಟ್ಟಿಂಗ್ಗಳಿಂದ ತೆರೆಯಿರಿ"</string> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index 4bcc76dd0259..8f36aab7ce8a 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"화면 분할"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"앱을 여기로 이동할 수 없음"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"최대화하기"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"복원"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"왼쪽으로 맞추기"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"오른쪽으로 맞추기"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"기본값으로 열기 설정"</string> diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index 6ae51a4ab770..c1219ba86249 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды сүрөткө тартып алуу"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Колдонмону бул жерге жылдырууга болбойт"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Чоңойтуу"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Калыбына келтирүү"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солго жылдыруу"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Оңго жылдыруу"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Демейки шартта ачуу параметрлери"</string> diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml index f8a09da308b9..375fc6435935 100644 --- a/libs/WindowManager/Shell/res/values-lo/strings.xml +++ b/libs/WindowManager/Shell/res/values-lo/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ສະແນັບໜ້າຈໍ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ບໍ່ສາມາດຍ້າຍແອັບມາບ່ອນນີ້ໄດ້"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ແນບຊ້າຍ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ແນບຂວາ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ເປີດຕາມການຕັ້ງຄ່າເລີ່ມຕົ້ນ"</string> diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml index 857e90e6d340..dfc3b45786de 100644 --- a/libs/WindowManager/Shell/res/values-lt/strings.xml +++ b/libs/WindowManager/Shell/res/values-lt/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Sutraukti ekraną"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Programos negalima perkelti čia"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Padidinti"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Atkurti"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pritraukti kairėje"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pritraukti dešinėje"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Atidaryti pagal numatytuosius nustatymus"</string> diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml index e56363e06c46..87818524cabe 100644 --- a/libs/WindowManager/Shell/res/values-lv/strings.xml +++ b/libs/WindowManager/Shell/res/values-lv/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fiksēt ekrānu"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Lietotni nevar pārvietot šeit."</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizēt"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Atjaunot"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Piestiprināt pa kreisi"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Piestiprināt pa labi"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Atvērt pēc noklusējuma iestatījumiem"</string> diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml index 1bf7a28aa410..88fed7414758 100644 --- a/libs/WindowManager/Shell/res/values-mk/strings.xml +++ b/libs/WindowManager/Shell/res/values-mk/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Подели го екранот на половина"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликацијата не може да се премести овде"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Максимизирај"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Врати"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Фотографирај лево"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Фотографирај десно"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Отвори според стандардните поставки"</string> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index 3401f6d6b54a..71fb78eca0f3 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"സ്ക്രീൻ സ്നാപ്പ് ചെയ്യുക"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ആപ്പ് ഇവിടേക്ക് നീക്കാനാകില്ല"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"വലുതാക്കുക"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"പുനഃസ്ഥാപിക്കുക"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ഇടതുവശത്തേക്ക് സ്നാപ്പ് ചെയ്യുക"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"വലതുവശത്തേക്ക് സ്നാപ്പ് ചെയ്യുക"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ഡിഫോൾട്ട് ക്രമീകരണം ഉപയോഗിച്ച് തുറക്കുക"</string> diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml index 87708d0a0cc2..04f2f8202961 100644 --- a/libs/WindowManager/Shell/res/values-mn/strings.xml +++ b/libs/WindowManager/Shell/res/values-mn/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Дэлгэцийг таллах"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Аппыг ийш зөөх боломжгүй"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Томруулах"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Зүүн тийш зэрэгцүүлэх"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Баруун тийш зэрэгцүүлэх"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Өгөгдмөл тохиргоогоор нээх"</string> diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml index 1ea41e557c4f..be1df3273b21 100644 --- a/libs/WindowManager/Shell/res/values-mr/strings.xml +++ b/libs/WindowManager/Shell/res/values-mr/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रीन स्नॅप करा"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"अॅप इथे हलवू शकत नाही"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"मोठे करा"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"रिस्टोअर करा"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"डावीकडे स्नॅप करा"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"उजवीकडे स्नॅप करा"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"बाय डीफॉल्ट सेटिंग्ज उघडा"</string> diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml index ca248e133eb8..04da8869d5a7 100644 --- a/libs/WindowManager/Shell/res/values-ms/strings.xml +++ b/libs/WindowManager/Shell/res/values-ms/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tangkap Skrin"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apl tidak boleh dialihkan ke sini"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimumkan"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Pulihkan"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Autojajar ke kiri"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Autojajar ke kanan"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buka tetapan secara lalai"</string> diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml index 3c4325bfc5b8..915a7cd3d67a 100644 --- a/libs/WindowManager/Shell/res/values-my/strings.xml +++ b/libs/WindowManager/Shell/res/values-my/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"စခရင်ကို ချုံ့မည်"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"အက်ပ်ကို ဤနေရာသို့ ရွှေ့၍မရပါ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ချဲ့ရန်"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ပြန်ပြောင်းရန်"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ဘယ်တွင် ချဲ့ရန်"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ညာတွင် ချဲ့ရန်"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"မူရင်းဆက်တင်ဖြင့် ဖွင့်ရန်"</string> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 4096bbf69fe7..8e5aee1eb49b 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fest skjermen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Appen kan ikke flyttes hit"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimer"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Gjenopprett"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fest til venstre"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fest til høyre"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Innstillinger for åpning som standard"</string> diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml index 2fc5e0902efa..42f2336c63ff 100644 --- a/libs/WindowManager/Shell/res/values-ne/strings.xml +++ b/libs/WindowManager/Shell/res/values-ne/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रिन स्न्याप गर्नुहोस्"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"एप सारेर यहाँ ल्याउन सकिएन"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ठुलो बनाउनुहोस्"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"रिस्टोर गर्नुहोस्"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बायाँतिर स्न्याप गर्नुहोस्"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"दायाँतिर स्न्याप गर्नुहोस्"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"डिफल्ट सेटिङअनुसार खोल्नुहोस्"</string> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index 65fd8ea44d2e..d19a4d44d6f9 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Scherm halveren"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Kan de app niet hierheen verplaatsen"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximaliseren"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Herstellen"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links uitlijnen"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Rechts uitlijnen"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Instellingen voor Standaard openen"</string> diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml index 1f96daad93b9..f67a6b2b7d38 100644 --- a/libs/WindowManager/Shell/res/values-or/strings.xml +++ b/libs/WindowManager/Shell/res/values-or/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ସ୍କ୍ରିନକୁ ସ୍ନାପ କରନ୍ତୁ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ଆପକୁ ଏଠାକୁ ମୁଭ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ବଡ଼ କରନ୍ତୁ"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ବାମରେ ସ୍ନାପ କରନ୍ତୁ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ଡାହାଣରେ ସ୍ନାପ କରନ୍ତୁ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ଡିଫଲ୍ଟ ସେଟିଂସକୁ ଖୋଲନ୍ତୁ"</string> diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml index f93f5097ac66..76d59af75bfb 100644 --- a/libs/WindowManager/Shell/res/values-pa/strings.xml +++ b/libs/WindowManager/Shell/res/values-pa/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ਸਕ੍ਰੀਨ ਨੂੰ ਸਨੈਪ ਕਰੋ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ਐਪ ਨੂੰ ਇੱਥੇ ਨਹੀਂ ਲਿਜਾਇਆ ਜਾ ਸਕਦਾ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ਵੱਡਾ ਕਰੋ"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ਖੱਬੇ ਪਾਸੇ ਸਨੈਪ ਕਰੋ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ਸੱਜੇ ਪਾਸੇ ਸਨੈਪ ਕਰੋ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੈਟਿੰਗਾਂ ਮੁਤਾਬਕ ਖੋਲ੍ਹੋ"</string> diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml index 1e76e82094d8..502e53abdcd3 100644 --- a/libs/WindowManager/Shell/res/values-pl/strings.xml +++ b/libs/WindowManager/Shell/res/values-pl/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Przyciągnij ekran"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Nie można przenieść aplikacji tutaj"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksymalizuj"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Przywróć"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Przyciągnij do lewej"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Przyciągnij do prawej"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ustawienia domyślnego otwierania"</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml index 0bdb0786ef71..3ec5e76bc5b4 100644 --- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar à direita"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configurações \"Abrir por padrão\""</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml index 0e10da1e6e0a..184a5b336fd8 100644 --- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar ecrã"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover a app para aqui"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Encaixar à esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Encaixar à direita"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Definições de Abrir por predefinição"</string> diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml index 0bdb0786ef71..3ec5e76bc5b4 100644 --- a/libs/WindowManager/Shell/res/values-pt/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar à direita"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configurações \"Abrir por padrão\""</string> diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index c94273630432..9703328d4d98 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Micșorează fereastra și fixeaz-o"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplicația nu poate fi mutată aici"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizează"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restabilește"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Trage la stânga"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Trage la dreapta"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Setări de deschidere în mod prestabilit"</string> diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml index e2c39387c8b4..401a8aab2576 100644 --- a/libs/WindowManager/Shell/res/values-ru/strings.xml +++ b/libs/WindowManager/Shell/res/values-ru/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Свернуть"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложение нельзя сюда переместить"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Развернуть"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Восстановить"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Привязать слева"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Привязать справа"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Настройки, регулирующие, как по умолчанию открываются ссылки"</string> diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml index 83a09f5beffe..c101b4cd757c 100644 --- a/libs/WindowManager/Shell/res/values-si/strings.xml +++ b/libs/WindowManager/Shell/res/values-si/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ස්නැප් තිරය"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"යෙදුම මෙතැනට ගෙන යා නොහැක"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"විහිදන්න"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"වමට ස්නැප් කරන්න"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"දකුණට ස්නැප් කරන්න"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"පෙරනිමි සැකසීම් මඟින් විවෘත කරන්න"</string> diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml index 1b3907e5775b..7214300f1eb7 100644 --- a/libs/WindowManager/Shell/res/values-sk/strings.xml +++ b/libs/WindowManager/Shell/res/values-sk/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zobraziť polovicu obrazovky"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikácia sa sem nedá presunúť"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovať"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnoviť"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prichytiť vľavo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Prichytiť vpravo"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvárať podľa predvolených nastavení"</string> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index 0a1b4a691313..04fe7e89c16e 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Pripni zaslon"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacije ni mogoče premakniti sem"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiraj"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnovi"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pripni levo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pripni desno"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Nastavitve privzetega odpiranja"</string> diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml index 75120d2418b5..6662ca1a059d 100644 --- a/libs/WindowManager/Shell/res/values-sq/strings.xml +++ b/libs/WindowManager/Shell/res/values-sq/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Regjistro ekranin"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacioni nuk mund të zhvendoset këtu"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizo"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Zhvendos majtas"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Zhvendos djathtas"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Hap sipas cilësimeve të parazgjedhura"</string> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index 8b5c4dfff363..d0a4ae684af7 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Уклопи екран"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликација не може да се премести овде"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увећајте"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Вратите"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прикачите лево"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Прикачите десно"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Подешавање Подразумевано отварај"</string> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index e40b6492fc71..6ce2a9a1cb4d 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fäst skärmen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Det går inte att flytta appen hit"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Utöka"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Återställ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fäst till vänster"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fäst till höger"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Inställningar för Öppna som standard"</string> diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml index e63229ccf2cd..40967f02be45 100644 --- a/libs/WindowManager/Shell/res/values-sw/strings.xml +++ b/libs/WindowManager/Shell/res/values-sw/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Panga Madirisha kwenye Skrini"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Imeshindwa kuhamishia programu hapa"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Panua"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Rejesha"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Telezesha kushoto"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Telezesha kulia"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Fungua kwa mipangilio chaguomsingi"</string> diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml index 95972f1f9486..3140c2c77fae 100644 --- a/libs/WindowManager/Shell/res/values-ta/strings.xml +++ b/libs/WindowManager/Shell/res/values-ta/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"திரையை ஸ்னாப் செய்"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ஆப்ஸை இங்கே நகர்த்த முடியாது"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"பெரிதாக்கும்"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"மீட்டெடுக்கும்"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"இடதுபுறம் நகர்த்தும்"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"வலதுபுறம் நகர்த்தும்"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"இயல்பாக அமைப்புகளைத் திறக்கும்"</string> diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index 6223c83d7599..62e62c7a1e25 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"స్క్రీన్ను స్నాప్ చేయండి"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"యాప్ను ఇక్కడకి తరలించడం సాధ్యం కాదు"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"మ్యాగ్జిమైజ్ చేయండి"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"రీస్టోర్ చేయండి"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ఎడమ వైపున స్నాప్ చేయండి"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"కుడి వైపున స్నాప్ చేయండి"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ఆటోమేటిక్ సెట్టింగ్ల ద్వారా తెరవండి"</string> diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml index f74499c0ddbf..e6386a20e492 100644 --- a/libs/WindowManager/Shell/res/values-th/strings.xml +++ b/libs/WindowManager/Shell/res/values-th/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"สแนปหน้าจอ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ย้ายแอปมาที่นี่ไม่ได้"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ขยายใหญ่สุด"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"คืนค่า"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"จัดพอดีกับทางซ้าย"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"จัดพอดีกับทางขวา"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"เปิดตามการตั้งค่าเริ่มต้น"</string> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index 7d984e0a1c14..176be336117d 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"I-snap ang Screen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Hindi mailipat dito ang app"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"I-maximize"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"I-restore"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"I-snap pakaliwa"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"I-snap pakanan"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buksan sa pamamagitan ng mga default na setting"</string> diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml index ba186aae80c8..73248e3d0c96 100644 --- a/libs/WindowManager/Shell/res/values-tr/strings.xml +++ b/libs/WindowManager/Shell/res/values-tr/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranın Yarısına Tuttur"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Uygulama buraya taşınamıyor"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ekranı kapla"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Geri yükle"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tuttur"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Sağa tuttur"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Varsayılan olarak açma ayarları"</string> diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml index 756e64da4574..a655a3eb452c 100644 --- a/libs/WindowManager/Shell/res/values-uk/strings.xml +++ b/libs/WindowManager/Shell/res/values-uk/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Зафіксувати екран"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Сюди не можна перемістити додаток"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Розгорнути"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Відновити"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Закріпити ліворуч"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Закріпити праворуч"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Налаштування \"Відкривати за умовчанням\""</string> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index 8aaa306a2ada..4bdea83e59e3 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"اسکرین کا اسناپ شاٹ لیں"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ایپ کو یہاں منتقل نہیں کیا جا سکتا"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بڑا کریں"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"بحال کریں"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"دائیں منتقل کریں"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"بائیں منتقل کریں"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"بطور ڈیفالٹ ترتیبات کھولیں"</string> diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml index 4e4a58ba25dc..b3a496f27582 100644 --- a/libs/WindowManager/Shell/res/values-uz/strings.xml +++ b/libs/WindowManager/Shell/res/values-uz/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranni biriktirish"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ilova bu yerga surilmaydi"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Yoyish"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Tiklash"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chapga tortish"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Oʻngga tortish"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Birlamchi sozlamalar asosida ochish"</string> diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml index 09a143af61a0..36d66e432590 100644 --- a/libs/WindowManager/Shell/res/values-vi/strings.xml +++ b/libs/WindowManager/Shell/res/values-vi/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Điều chỉnh kích thước màn hình"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Không di chuyển được ứng dụng đến đây"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Phóng to tối đa"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Khôi phục"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Di chuyển nhanh sang trái"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Di chuyển nhanh sang phải"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Mở các chế độ cài đặt theo mặc định"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml index 795febb0ee3f..64446cd4b342 100644 --- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"屏幕快照"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"无法将应用移至此处"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"恢复"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"贴靠左侧"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"贴靠右侧"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"默认打开设置"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index 0c6ad618ec58..4970e8b92afb 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至這裡"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"還原"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"貼齊左邊"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"貼齊右邊"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"採用預設設定打開"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index 442a6feae787..fcdcccaee5f5 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至此處"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"還原"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"靠左對齊"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"靠右對齊"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"開啟連結的預設設定"</string> diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml index 47613d451a33..cbc6c022a3c6 100644 --- a/libs/WindowManager/Shell/res/values-zu/strings.xml +++ b/libs/WindowManager/Shell/res/values-zu/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Thwebula Isikrini"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"I-app ayikwazi ukuhanjiswa lapha"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Khulisa"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Buyisela"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chofoza kwesobunxele"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Chofoza kwesokudla"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Vula amasethingi ngokuzenzakalela"</string> diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index a14461a57a95..86751d45f378 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -45,6 +45,9 @@ <!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu --> <bool name="config_pipEnableResizeForMenu">true</bool> + <!-- Allow PIP to resize via dragging the corner of PiP. --> + <bool name="config_pipEnableDragCornerResize">false</bool> + <!-- Time (duration in milliseconds) that the shell waits for an app to close the PiP by itself if a custom action is present before closing it. --> <integer name="config_pipForceCloseDelay">1000</integer> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index df1e2248872b..d02c77e831aa 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -240,8 +240,14 @@ <dimen name="bubble_popup_content_max_width">300dp</dimen> <!-- Horizontal margin for the bubble popup view. --> <dimen name="bubble_popup_margin_horizontal">32dp</dimen> - <!-- Top margin for the bubble popup view. --> - <dimen name="bubble_popup_margin_top">16dp</dimen> + <!-- Top margin for the bubble bar education views. --> + <dimen name="bubble_popup_margin_top">24dp</dimen> + <!-- Bottom margin for the bubble bar education views. --> + <dimen name="bubble_popup_margin_bottom">32dp</dimen> + <!-- Text margin for the bubble bar education views. --> + <dimen name="bubble_popup_text_margin">16dp</dimen> + <!-- Size of icons in the bubble bar education views. --> + <dimen name="bubble_popup_icon_size">32dp</dimen> <!-- Width for the bubble popup view arrow. --> <dimen name="bubble_popup_arrow_width">12dp</dimen> <!-- Height for the bubble popup view arrow. --> @@ -250,6 +256,8 @@ <dimen name="bubble_popup_arrow_corner_radius">2dp</dimen> <!-- Padding for the bubble popup view contents. --> <dimen name="bubble_popup_padding">24dp</dimen> + <!-- Elevation of the popup user education views for the bubble bar --> + <dimen name="bubble_popup_elevation">2dp</dimen> <!-- The size of the caption bar inset at the top of bubble bar expanded view. --> <dimen name="bubble_bar_expanded_view_caption_height">36dp</dimen> <!-- The width of the caption bar at the top of bubble bar expanded view. --> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt index 79becb0a2e20..0e8e90467745 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt @@ -122,7 +122,8 @@ abstract class ManageWindowsViewContainer( snapshot.hardwareBuffer, snapshot.colorSpace ) - val scaledSnapshotBitmap = snapshotBitmap?.let { + val croppedBitmap = snapshotBitmap?.let { cropBitmap(it) } + val scaledSnapshotBitmap = croppedBitmap?.let { Bitmap.createScaledBitmap( it, instanceIconWidth.toInt(), instanceIconHeight.toInt(), true /* filter */ ) @@ -160,6 +161,35 @@ abstract class ManageWindowsViewContainer( menuHeight += iconMargin.toInt() } + private fun cropBitmap( + bitmapToCrop: Bitmap + ): Bitmap { + val ratioToMatch = ICON_WIDTH_DP / ICON_HEIGHT_DP + val bitmapWidth = bitmapToCrop.width + val bitmapHeight = bitmapToCrop.height + if (bitmapWidth > bitmapHeight * ratioToMatch) { + // Crop based on height + val newWidth = bitmapHeight * ratioToMatch + return Bitmap.createBitmap( + bitmapToCrop, + ((bitmapWidth - newWidth) / 2).toInt(), + 0, + newWidth.toInt(), + bitmapHeight + ) + } else { + // Crop based on width + val newHeight = bitmapWidth / ratioToMatch + return Bitmap.createBitmap( + bitmapToCrop, + 0, + ((bitmapHeight - newHeight) / 2).toInt(), + bitmapWidth, + newHeight.toInt() + ) + } + } + companion object { private const val MENU_RADIUS_DP = 26f private const val ICON_WIDTH_DP = 204f 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 e4db7b636ed9..5eb5d8962b55 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 @@ -95,6 +95,7 @@ import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; @@ -120,7 +121,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @ShellMainThread private final Handler mHandler; /** True when a back gesture is ongoing */ - private boolean mBackGestureStarted = false; + @VisibleForTesting public boolean mBackGestureStarted = false; /** Tracks if an uninterruptible animation is in progress */ private boolean mPostCommitAnimationInProgress = false; @@ -511,6 +512,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Ignoring MotionEvent because two gestures are already being queued."); return; + } else if (mBackGestureStarted && mCurrentTracker.isInInitialState() + && mQueuedTracker.isInInitialState()) { + ProtoLog.e(WM_SHELL_BACK_PREVIEW, + "Both touch trackers in initial state and mBackGestureStarted=true"); + mBackGestureStarted = false; } if (keyAction == MotionEvent.ACTION_DOWN) { @@ -985,7 +991,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()"); mActiveCallback = null; mApps = null; - mShouldStartOnNextMoveEvent = false; mOnBackStartDispatched = false; mThresholdCrossed = false; mPointersPilfered = false; @@ -1273,42 +1278,42 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } // Copy initial changes to final transition final TransitionInfo init = mOpenTransitionInfo; - // find prepare open target + // Find prepare open target boolean openShowWallpaper = false; - ComponentName openComponent = null; + final ArrayList<OpenChangeInfo> targets = new ArrayList<>(); int tmpSize; - int openTaskId = INVALID_TASK_ID; - WindowContainerToken openToken = null; for (int j = init.getChanges().size() - 1; j >= 0; --j) { final TransitionInfo.Change change = init.getChanges().get(j); - if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { - openComponent = findComponentName(change); - openTaskId = findTaskId(change); - openToken = findToken(change); + if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED) + && TransitionUtil.isOpeningMode(change.getMode())) { + final ComponentName openComponent = findComponentName(change); + final int openTaskId = findTaskId(change); + final WindowContainerToken openToken = findToken(change); + if (openComponent == null && openTaskId == INVALID_TASK_ID + && openToken == null) { + continue; + } + targets.add(new OpenChangeInfo(openComponent, openTaskId, openToken)); if (change.hasFlags(FLAG_SHOW_WALLPAPER)) { openShowWallpaper = true; } - break; } } - if (openComponent == null && openTaskId == INVALID_TASK_ID && openToken == null) { + if (targets.isEmpty()) { // This shouldn't happen, but if that happen, consume the initial transition anyway. Log.e(TAG, "Unable to merge following transition, cannot find the gesture " + "animated target from the open transition=" + mOpenTransitionInfo); mOpenTransitionInfo = null; return; } - // find first non-prepare open target + // Find first non-prepare open target boolean isOpen = false; tmpSize = info.getChanges().size(); for (int j = 0; j < tmpSize; ++j) { final TransitionInfo.Change change = info.getChanges().get(j); - final ComponentName firstNonOpen = findComponentName(change); - final int firstTaskId = findTaskId(change); - if ((firstNonOpen != null && firstNonOpen != openComponent) - || (firstTaskId != INVALID_TASK_ID && firstTaskId != openTaskId)) { - // this is original close target, potential be close, but cannot determine from - // it + if (isOpenChangeMatched(targets, change)) { + // This is original close target, potential be close, but cannot determine + // from it. if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { isOpen = !TransitionUtil.isClosingMode(change.getMode()); } else { @@ -1317,33 +1322,44 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } } } - if (!isOpen) { // Close transition, the transition info should be: // init info(open A & wallpaper) // current info(close B target) // remove init info(open/change A target & wallpaper) boolean moveToTop = false; + boolean excludeOpenTarget = false; + boolean mergePredictive = false; for (int j = info.getChanges().size() - 1; j >= 0; --j) { final TransitionInfo.Change change = info.getChanges().get(j); - if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) { + if (isOpenChangeMatched(targets, change)) { + if (TransitionUtil.isClosingMode(change.getMode())) { + excludeOpenTarget = true; + } moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP); info.getChanges().remove(j); } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER)) || !change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { info.getChanges().remove(j); + } else if (!mergePredictive && TransitionUtil.isClosingMode(change.getMode())) { + mergePredictive = true; } } // Ignore merge if there is no close target - if (!info.getChanges().isEmpty()) { + if (!info.getChanges().isEmpty() && mergePredictive) { tmpSize = init.getChanges().size(); for (int i = 0; i < tmpSize; ++i) { final TransitionInfo.Change change = init.getChanges().get(i); if (change.hasFlags(FLAG_IS_WALLPAPER)) { continue; } - if (moveToTop) { - if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) { + if (isOpenChangeMatched(targets, change)) { + if (excludeOpenTarget) { + // App has triggered another change during predictive back + // transition, filter out predictive back target. + continue; + } + if (moveToTop) { change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP); } } @@ -1372,7 +1388,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (nonBackClose && nonBackOpen) { for (int j = info.getChanges().size() - 1; j >= 0; --j) { final TransitionInfo.Change change = info.getChanges().get(j); - if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) { + if (isOpenChangeMatched(targets, change)) { info.getChanges().remove(j); } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) { info.getChanges().remove(j); @@ -1655,9 +1671,21 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont final ComponentName openChange = findComponentName(change); final int firstTaskId = findTaskId(change); final WindowContainerToken openToken = findToken(change); - return (openChange != null && openChange == topActivity) + return (openChange != null && openChange.equals(topActivity)) || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId) - || (openToken != null && token == openToken); + || (openToken != null && openToken.equals(token)); + } + + static boolean isOpenChangeMatched(@NonNull ArrayList<OpenChangeInfo> targets, + TransitionInfo.Change change) { + for (int i = targets.size() - 1; i >= 0; --i) { + final OpenChangeInfo next = targets.get(i); + if (isSameChangeTarget(next.mOpenComponent, next.mOpenTaskId, next.mOpenToken, + change)) { + return true; + } + } + return false; } private static boolean canBeTransitionTarget(TransitionInfo.Change change) { @@ -1717,4 +1745,16 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } } } + + static class OpenChangeInfo { + final ComponentName mOpenComponent; + final int mOpenTaskId; + final WindowContainerToken mOpenToken; + OpenChangeInfo(ComponentName openComponent, int openTaskId, + WindowContainerToken openToken) { + mOpenComponent = openComponent; + mOpenTaskId = openTaskId; + mOpenToken = openToken; + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java index 7a569799ab84..dc50fdbd1af3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java @@ -16,12 +16,15 @@ package com.android.wm.shell.back; +import static android.view.MotionEvent.ACTION_MOVE; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.window.BackEvent.EDGE_RIGHT; import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_TASK; +import static com.android.window.flags.Flags.predictiveBackTimestampApi; import static com.android.wm.shell.back.BackAnimationConstants.UPDATE_SYSUI_FLAGS_THRESHOLD; +import static com.android.wm.shell.back.CrossActivityBackAnimationKt.scaleCentered; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; import android.animation.Animator; @@ -36,11 +39,14 @@ import android.graphics.Rect; import android.graphics.RectF; import android.os.Handler; import android.os.RemoteException; +import android.util.TimeUtils; import android.view.Choreographer; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; +import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; +import android.view.VelocityTracker; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.window.BackEvent; @@ -48,6 +54,9 @@ import android.window.BackMotionEvent; import android.window.BackProgressAnimator; import android.window.IOnBackInvokedCallback; +import com.android.internal.dynamicanimation.animation.FloatValueHolder; +import com.android.internal.dynamicanimation.animation.SpringAnimation; +import com.android.internal.dynamicanimation.animation.SpringForce; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.policy.SystemBarUtils; import com.android.internal.protolog.ProtoLog; @@ -81,6 +90,11 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { /** Duration of post animation after gesture committed. */ private static final int POST_ANIMATION_DURATION_MS = 500; + private static final float SPRING_SCALE = 100f; + private static final float DEFAULT_FLING_VELOCITY = 320f; + private static final float MAX_FLING_VELOCITY = 1000f; + private static final float FLING_SPRING_STIFFNESS = 320f; + private final Rect mStartTaskRect = new Rect(); private float mCornerRadius; private int mStatusbarHeight; @@ -114,6 +128,14 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { private float mInterWindowMargin; private float mVerticalMargin; + private final FloatValueHolder mPostCommitFlingScale = new FloatValueHolder(SPRING_SCALE); + private final SpringForce mPostCommitFlingSpring = new SpringForce(SPRING_SCALE) + .setStiffness(FLING_SPRING_STIFFNESS) + .setDampingRatio(1f); + private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); + private float mGestureProgress = 0f; + private long mDownTime = 0L; + @Inject public CrossTaskBackAnimation(Context context, BackAnimationBackground background, @ShellMainThread Handler handler) { @@ -168,6 +190,7 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { if (mEnteringTarget == null || mClosingTarget == null) { return; } + mGestureProgress = progress; float touchY = event.getTouchY(); @@ -229,6 +252,8 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { } mClosingCurrentRect.set(left, top, left + width, top + height); + + applyFlingScale(mClosingCurrentRect); applyTransform(mClosingTarget.leash, mClosingCurrentRect, mCornerRadius); } @@ -239,9 +264,19 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { float height = mapRange(progress, mEnteringStartRect.height(), mStartTaskRect.height()); mEnteringCurrentRect.set(left, top, left + width, top + height); + + applyFlingScale(mEnteringCurrentRect); applyTransform(mEnteringTarget.leash, mEnteringCurrentRect, mCornerRadius); } + private void applyFlingScale(RectF rect) { + // apply a scale to the rect to account for fling velocity + final float flingScale = Math.min(mPostCommitFlingScale.getValue() / SPRING_SCALE, 1f); + if (flingScale >= 1f) return; + scaleCentered(rect, flingScale, /* pivotX */ rect.right, + /* pivotY */ rect.top + rect.height() / 2); + } + /** Transform the target window to match the target rect. */ private void applyTransform(SurfaceControl leash, RectF targetRect, float cornerRadius) { if (leash == null || !leash.isValid()) { @@ -280,6 +315,9 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { mTransformMatrix.reset(); mClosingCurrentRect.setEmpty(); mInitialTouchPos.set(0, 0); + mGestureProgress = 0; + mDownTime = 0; + mVelocityTracker.clear(); if (mFinishCallback != null) { try { @@ -295,10 +333,24 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { private void onGestureProgress(@NonNull BackEvent backEvent) { if (!mBackInProgress) { mBackInProgress = true; + mDownTime = backEvent.getFrameTimeMillis(); } float progress = backEvent.getProgress(); mTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY()); - updateGestureBackProgress(getInterpolatedProgress(progress), backEvent); + float interpolatedProgress = getInterpolatedProgress(progress); + if (predictiveBackTimestampApi()) { + mVelocityTracker.addMovement( + MotionEvent.obtain( + /* downTime */ mDownTime, + /* eventTime */ backEvent.getFrameTimeMillis(), + /* action */ ACTION_MOVE, + /* x */ interpolatedProgress * SPRING_SCALE, + /* y */ 0f, + /* metaState */ 0 + ) + ); + } + updateGestureBackProgress(interpolatedProgress, backEvent); } private void onGestureCommitted() { @@ -307,6 +359,25 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { return; } + if (predictiveBackTimestampApi()) { + // kick off spring animation with the current velocity from the pre-commit phase, this + // affects the scaling of the closing and/or opening task during post-commit + mVelocityTracker.computeCurrentVelocity(1000); + float startVelocity = mGestureProgress < 0.1f + ? -DEFAULT_FLING_VELOCITY : -mVelocityTracker.getXVelocity(); + SpringAnimation flingAnimation = + new SpringAnimation(mPostCommitFlingScale, SPRING_SCALE) + .setStartVelocity(Math.max(-MAX_FLING_VELOCITY, Math.min(0f, startVelocity))) + .setStartValue(SPRING_SCALE) + .setMinimumVisibleChange(0.1f) + .setSpring(mPostCommitFlingSpring); + flingAnimation.start(); + // do an animation-frame immediately to prevent idle frame + flingAnimation.doAnimationFrame( + Choreographer.getInstance().getLastFrameTimeNanos() / TimeUtils.NANOS_PER_MS + ); + } + // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current // coordinate of the gesture driven phase. mEnteringCurrentRect.round(mEnteringStartRect); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt index fd110a276826..9b3054e9ee13 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt @@ -27,7 +27,7 @@ fun BubblePopupView.setup() { val attrs = context.obtainStyledAttributes( intArrayOf( - com.android.internal.R.attr.materialColorSurface, + com.android.internal.R.attr.materialColorSurfaceContainer, android.R.attr.dialogCornerRadius ) ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt index 9fd255ded0ad..7adec39c9d66 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt @@ -20,6 +20,7 @@ import android.content.Context import android.graphics.Point import android.graphics.Rect import android.util.Log +import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -102,14 +103,17 @@ class BubbleEducationViewController(private val context: Context, private val li hideEducation(animated = false) log { "showStackEducation at: $position" } + val rootBounds = Rect() + // Get root bounds on screen as position is in screen coordinates + root.getBoundsOnScreen(rootBounds) educationView = createEducationView(R.layout.bubble_bar_stack_education, root).apply { setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN) - setArrowPosition(BubblePopupDrawable.ArrowPosition.End) - updateEducationPosition(view = this, position, root) + updateEducationPosition(view = this, position, rootBounds) val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f doOnLayout { - it.pivotX = it.width - arrowToEdgeOffset + it.pivotX = if (position.x < rootBounds.centerX()) + arrowToEdgeOffset else it.width - arrowToEdgeOffset it.pivotY = it.height.toFloat() } setOnClickListener { educationClickHandler() } @@ -218,12 +222,9 @@ class BubbleEducationViewController(private val context: Context, private val li * * @param view the user education view to layout * @param position the reference position in Screen coordinates - * @param root the root view to use for the layout + * @param rootBounds bounds of the parent the education view is placed in */ - private fun updateEducationPosition(view: BubblePopupView, position: Point, root: ViewGroup) { - val rootBounds = Rect() - // Get root bounds on screen as position is in screen coordinates - root.getBoundsOnScreen(rootBounds) + private fun updateEducationPosition(view: BubblePopupView, position: Point, rootBounds: Rect) { // Get the offset to the arrow from the edge of the education view val arrowToEdgeOffset = view.popupDrawable?.config?.let { it.cornerRadius + it.arrowWidth / 2f }?.roundToInt() @@ -231,7 +232,15 @@ class BubbleEducationViewController(private val context: Context, private val li // Calculate education view margins val params = view.layoutParams as FrameLayout.LayoutParams params.bottomMargin = rootBounds.bottom - position.y - params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset + if (position.x < rootBounds.centerX()) { + params.leftMargin = position.x - arrowToEdgeOffset + params.gravity = Gravity.LEFT or Gravity.BOTTOM + view.setArrowPosition(BubblePopupDrawable.ArrowPosition.Start) + } else { + params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset + params.gravity = Gravity.RIGHT or Gravity.BOTTOM + view.setArrowPosition(BubblePopupDrawable.ArrowPosition.End) + } view.layoutParams = params } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIStatusManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIStatusManager.java index 915a8a149d54..37369d1f3047 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIStatusManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIStatusManager.java @@ -24,6 +24,7 @@ import java.util.function.IntSupplier; /** Handle the visibility state of the Compat UI components. */ public class CompatUIStatusManager { + private static final int COMPAT_UI_EDUCATION_UNDEFINED = -1; public static final int COMPAT_UI_EDUCATION_HIDDEN = 0; public static final int COMPAT_UI_EDUCATION_VISIBLE = 1; @@ -32,24 +33,40 @@ public class CompatUIStatusManager { @NonNull private final IntSupplier mReader; + private int mCurrentValue = COMPAT_UI_EDUCATION_UNDEFINED; + public CompatUIStatusManager(@NonNull IntConsumer writer, @NonNull IntSupplier reader) { mWriter = writer; mReader = reader; } public CompatUIStatusManager() { - this(i -> { }, () -> COMPAT_UI_EDUCATION_HIDDEN); + this(i -> { + }, () -> COMPAT_UI_EDUCATION_HIDDEN); } void onEducationShown() { - mWriter.accept(COMPAT_UI_EDUCATION_VISIBLE); + if (mCurrentValue != COMPAT_UI_EDUCATION_VISIBLE) { + mCurrentValue = COMPAT_UI_EDUCATION_VISIBLE; + mWriter.accept(mCurrentValue); + } } void onEducationHidden() { - mWriter.accept(COMPAT_UI_EDUCATION_HIDDEN); + if (mCurrentValue != COMPAT_UI_EDUCATION_HIDDEN) { + mCurrentValue = COMPAT_UI_EDUCATION_HIDDEN; + mWriter.accept(mCurrentValue); + } } boolean isEducationVisible() { - return mReader.getAsInt() == COMPAT_UI_EDUCATION_VISIBLE; + return getCurrentValue() == COMPAT_UI_EDUCATION_VISIBLE; + } + + private int getCurrentValue() { + if (mCurrentValue == COMPAT_UI_EDUCATION_UNDEFINED) { + mCurrentValue = mReader.getAsInt(); + } + return mCurrentValue; } -} +}
\ No newline at end of file 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 4c2588984500..7f547868b3e2 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 @@ -17,7 +17,6 @@ package com.android.wm.shell.dagger; import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS; -import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS; import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT; import android.annotation.Nullable; @@ -67,7 +66,7 @@ import com.android.wm.shell.dagger.pip.PipModule; import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler; import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler; import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; -import com.android.wm.shell.desktopmode.DesktopFullImmersiveTransitionHandler; +import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler; import com.android.wm.shell.desktopmode.DesktopModeDragAndDropTransitionHandler; import com.android.wm.shell.desktopmode.DesktopModeEventLogger; @@ -397,12 +396,12 @@ public abstract class WMShellModule { Context context, ShellInit shellInit, Transitions transitions, - Optional<DesktopFullImmersiveTransitionHandler> desktopImmersiveTransitionHandler, + Optional<DesktopImmersiveController> desktopImmersiveController, WindowDecorViewModel windowDecorViewModel, Optional<TaskChangeListener> taskChangeListener, FocusTransitionObserver focusTransitionObserver) { return new FreeformTaskTransitionObserver( - context, shellInit, transitions, desktopImmersiveTransitionHandler, + context, shellInit, transitions, desktopImmersiveController, windowDecorViewModel, taskChangeListener, focusTransitionObserver); } @@ -638,7 +637,7 @@ public abstract class WMShellModule { ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler, DragToDesktopTransitionHandler dragToDesktopTransitionHandler, @DynamicOverride DesktopRepository desktopRepository, - Optional<DesktopFullImmersiveTransitionHandler> desktopFullImmersiveTransitionHandler, + Optional<DesktopImmersiveController> desktopImmersiveController, DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver, LaunchAdjacentController launchAdjacentController, RecentsTransitionHandler recentsTransitionHandler, @@ -657,7 +656,7 @@ public abstract class WMShellModule { returnToDragStartAnimator, enterDesktopTransitionHandler, exitDesktopTransitionHandler, desktopModeDragAndDropTransitionHandler, toggleResizeDesktopTaskTransitionHandler, - dragToDesktopTransitionHandler, desktopFullImmersiveTransitionHandler.get(), + dragToDesktopTransitionHandler, desktopImmersiveController.get(), desktopRepository, desktopModeLoggerTransitionObserver, launchAdjacentController, recentsTransitionHandler, multiInstanceHelper, mainExecutor, desktopTasksLimiter, @@ -705,7 +704,7 @@ public abstract class WMShellModule { @WMSingleton @Provides - static Optional<DesktopFullImmersiveTransitionHandler> provideDesktopImmersiveHandler( + static Optional<DesktopImmersiveController> provideDesktopImmersiveController( Context context, Transitions transitions, @DynamicOverride DesktopRepository desktopRepository, @@ -713,7 +712,7 @@ public abstract class WMShellModule { ShellTaskOrganizer shellTaskOrganizer) { if (DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.of( - new DesktopFullImmersiveTransitionHandler( + new DesktopImmersiveController( transitions, desktopRepository, displayController, @@ -848,8 +847,7 @@ public abstract class WMShellModule { InteractionJankMonitor interactionJankMonitor, @ShellMainThread Handler handler ) { - if (!DesktopModeStatus.canEnterDesktopMode(context) - || !ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS.isTrue()) { + if (!DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.empty(); } return Optional.of( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt index 9d4926b47def..d0bc5f0955f7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt @@ -36,20 +36,21 @@ import com.android.wm.shell.common.DisplayController import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.TransitionHandler +import com.android.wm.shell.transition.Transitions.TransitionObserver import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener /** - * A [TransitionHandler] to move a task in/out of desktop's full immersive state where the task + * A controller to move tasks in/out of desktop's full immersive state where the task * remains freeform while being able to take fullscreen bounds and have its App Header visibility * be transient below the status bar like in fullscreen immersive mode. */ -class DesktopFullImmersiveTransitionHandler( +class DesktopImmersiveController( private val transitions: Transitions, private val desktopRepository: DesktopRepository, private val displayController: DisplayController, private val shellTaskOrganizer: ShellTaskOrganizer, private val transactionSupplier: () -> SurfaceControl.Transaction, -) : TransitionHandler { +) : TransitionHandler, TransitionObserver { constructor( transitions: Transitions, @@ -67,7 +68,7 @@ class DesktopFullImmersiveTransitionHandler( private var state: TransitionState? = null @VisibleForTesting - val pendingExternalExitTransitions = mutableSetOf<ExternalPendingExit>() + val pendingExternalExitTransitions = mutableListOf<ExternalPendingExit>() /** Whether there is an immersive transition that hasn't completed yet. */ private val inProgress: Boolean @@ -137,14 +138,19 @@ class DesktopFullImmersiveTransitionHandler( * * @param wct that will apply these changes * @param displayId of the display that should exit immersive mode + * @param excludeTaskId of the task to ignore (not exit) if it is the immersive one * @return a function to apply once the transition that will apply these changes is started */ fun exitImmersiveIfApplicable( wct: WindowContainerTransaction, - displayId: Int + displayId: Int, + excludeTaskId: Int? = null, ): ((IBinder) -> Unit)? { if (!Flags.enableFullyImmersiveInDesktop()) return null val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId) ?: return null + if (immersiveTask == excludeTaskId) { + return null + } val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask) ?: return null logV("Appending immersive exit for task: $immersiveTask in display: $displayId") wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo)) @@ -179,6 +185,17 @@ class DesktopFullImmersiveTransitionHandler( return null } + + /** Whether the [change] in the [transition] is a known immersive change. */ + fun isImmersiveChange( + transition: IBinder, + change: TransitionInfo.Change, + ): Boolean { + return pendingExternalExitTransitions.any { + it.transition == transition && it.taskId == change.taskInfo?.taskId + } + } + private fun addPendingImmersiveExit(taskId: Int, displayId: Int, transition: IBinder) { pendingExternalExitTransitions.add( ExternalPendingExit( @@ -196,10 +213,11 @@ class DesktopFullImmersiveTransitionHandler( finishTransaction: SurfaceControl.Transaction, finishCallback: Transitions.TransitionFinishCallback ): Boolean { + logD("startAnimation transition=%s", transition) val state = requireState() if (transition != state.transition) return false animateResize( - transitionState = state, + targetTaskId = state.taskId, info = info, startTransaction = startTransaction, finishTransaction = finishTransaction, @@ -209,40 +227,55 @@ class DesktopFullImmersiveTransitionHandler( } private fun animateResize( - transitionState: TransitionState, + targetTaskId: Int, info: TransitionInfo, startTransaction: SurfaceControl.Transaction, finishTransaction: SurfaceControl.Transaction, finishCallback: Transitions.TransitionFinishCallback ) { + logD("animateResize for task#%d", targetTaskId) val change = info.changes.first { c -> val taskInfo = c.taskInfo - return@first taskInfo != null && taskInfo.taskId == transitionState.taskId + return@first taskInfo != null && taskInfo.taskId == targetTaskId } + animateResizeChange(change, startTransaction, finishTransaction, finishCallback) + } + + /** + * Animate an immersive change. + * + * As of now, both enter and exit transitions have the same animation, a veiled resize. + */ + fun animateResizeChange( + change: TransitionInfo.Change, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction, + finishCallback: Transitions.TransitionFinishCallback, + ) { + val taskId = change.taskInfo!!.taskId val leash = change.leash val startBounds = change.startAbsBounds val endBounds = change.endAbsBounds - + logD("Animating resize change for task#%d from %s to %s", taskId, startBounds, endBounds) + + startTransaction + .setPosition(leash, startBounds.left.toFloat(), startBounds.top.toFloat()) + .setWindowCrop(leash, startBounds.width(), startBounds.height()) + .show(leash) + onTaskResizeAnimationListener + ?.onAnimationStart(taskId, startTransaction, startBounds) + ?: startTransaction.apply() val updateTransaction = transactionSupplier() ValueAnimator.ofObject(rectEvaluator, startBounds, endBounds).apply { duration = FULL_IMMERSIVE_ANIM_DURATION_MS interpolator = DecelerateInterpolator() addListener( - onStart = { - startTransaction - .setPosition(leash, startBounds.left.toFloat(), startBounds.top.toFloat()) - .setWindowCrop(leash, startBounds.width(), startBounds.height()) - .show(leash) - onTaskResizeAnimationListener - ?.onAnimationStart(transitionState.taskId, startTransaction, startBounds) - ?: startTransaction.apply() - }, onEnd = { finishTransaction .setPosition(leash, endBounds.left.toFloat(), endBounds.top.toFloat()) .setWindowCrop(leash, endBounds.width(), endBounds.height()) .apply() - onTaskResizeAnimationListener?.onAnimationEnd(transitionState.taskId) + onTaskResizeAnimationListener?.onAnimationEnd(taskId) finishCallback.onTransitionFinished(null /* wct */) clearState() } @@ -254,7 +287,7 @@ class DesktopFullImmersiveTransitionHandler( .setWindowCrop(leash, rect.width(), rect.height()) .apply() onTaskResizeAnimationListener - ?.onBoundsChange(transitionState.taskId, updateTransaction, rect) + ?.onBoundsChange(taskId, updateTransaction, rect) ?: updateTransaction.apply() } start() @@ -284,15 +317,20 @@ class DesktopFullImmersiveTransitionHandler( * |onTransitionReady|, before this transition actually animates) because drawing decorations * depends on whether the task is in full immersive state or not. */ - fun onTransitionReady(transition: IBinder, info: TransitionInfo) { + override fun onTransitionReady( + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction, + ) { + logD("onTransitionReady transition=%s", transition) // Check if this is a pending external exit transition. val pendingExit = pendingExternalExitTransitions .firstOrNull { pendingExit -> pendingExit.transition == transition } if (pendingExit != null) { - pendingExternalExitTransitions.remove(pendingExit) if (info.hasTaskChange(taskId = pendingExit.taskId)) { if (desktopRepository.isTaskInFullImmersiveState(pendingExit.taskId)) { - logV("Pending external exit for task ${pendingExit.taskId} verified") + logV("Pending external exit for task#%d verified", pendingExit.taskId) desktopRepository.setTaskInFullImmersiveState( displayId = pendingExit.displayId, taskId = pendingExit.taskId, @@ -311,7 +349,7 @@ class DesktopFullImmersiveTransitionHandler( val state = requireState() val startBounds = info.changes.first { c -> c.taskInfo?.taskId == state.taskId } .startAbsBounds - logV("Direct move for task ${state.taskId} in ${state.direction} direction verified") + logV("Direct move for task#%d in %s direction verified", state.taskId, state.direction) when (state.direction) { Direction.ENTER -> { desktopRepository.setTaskInFullImmersiveState( @@ -343,7 +381,7 @@ class DesktopFullImmersiveTransitionHandler( .filter { c -> desktopRepository.isTaskInFullImmersiveState(c.taskInfo!!.taskId) } .filter { c -> c.startRotation != c.endRotation } .forEach { c -> - logV("Detected immersive exit due to rotation for task: ${c.taskInfo!!.taskId}") + logV("Detected immersive exit due to rotation for task#%d", c.taskInfo!!.taskId) desktopRepository.setTaskInFullImmersiveState( displayId = c.taskInfo!!.displayId, taskId = c.taskInfo!!.taskId, @@ -352,6 +390,32 @@ class DesktopFullImmersiveTransitionHandler( } } + override fun onTransitionMerged(merged: IBinder, playing: IBinder) { + logD("onTransitionMerged merged=%s playing=%s", merged, playing) + val pendingExit = pendingExternalExitTransitions + .firstOrNull { pendingExit -> pendingExit.transition == merged } + if (pendingExit != null) { + logV( + "Pending exit transition %s for task#%s merged into %s", + merged, pendingExit.taskId, playing + ) + pendingExit.transition = playing + } + } + + override fun onTransitionFinished(transition: IBinder, aborted: Boolean) { + logD("onTransitionFinished transition=%s aborted=%b", transition, aborted) + val pendingExit = pendingExternalExitTransitions + .firstOrNull { pendingExit -> pendingExit.transition == transition } + if (pendingExit != null) { + logV( + "Pending exit transition %s for task#%s finished", + transition, pendingExit + ) + pendingExternalExitTransitions.remove(pendingExit) + } + } + private fun clearState() { state = null } @@ -394,7 +458,7 @@ class DesktopFullImmersiveTransitionHandler( data class ExternalPendingExit( val taskId: Int, val displayId: Int, - val transition: IBinder, + var transition: IBinder, ) private enum class Direction { @@ -405,6 +469,10 @@ class DesktopFullImmersiveTransitionHandler( ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } + private fun logD(msg: String, vararg arguments: Any?) { + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + private companion object { private const val TAG = "DesktopImmersive" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt index df9fc59b925e..54a07f20fd84 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt @@ -23,6 +23,7 @@ import android.os.Handler import android.os.IBinder import android.view.SurfaceControl import android.view.WindowManager +import android.window.DesktopModeFlags import android.window.TransitionInfo import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction @@ -59,6 +60,9 @@ class DesktopMixedTransitionHandler( /** Starts close transition and handles or delegates desktop task close animation. */ override fun startRemoveTransition(wct: WindowContainerTransaction?): IBinder { + if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS.isTrue) { + return freeformTaskTransitionHandler.startRemoveTransition(wct) + } requireNotNull(wct) return transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, /* handler= */ this) } @@ -138,7 +142,7 @@ class DesktopMixedTransitionHandler( private fun isLastDesktopTask(change: TransitionInfo.Change): Boolean = change.taskInfo?.let { - desktopRepository.getActiveNonMinimizedTaskCount(it.displayId) == 1 + desktopRepository.getExpandedTaskCount(it.displayId) == 1 } ?: false private fun findCloseDesktopTaskChange(info: TransitionInfo): TransitionInfo.Change? { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt index 85a3126d9de6..6f7b7162d2ec 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt @@ -129,19 +129,24 @@ class DesktopRepository ( DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 } ?: desktop.zOrderedTasksCount + var visibleTasksCount = 0 desktop.zOrderedTasksList // Reverse it so we initialize the repo from bottom to top. .reversed() - .mapNotNull { taskId -> - desktop.tasksByTaskIdMap[taskId]?.takeIf { - it.desktopTaskState == DesktopTaskState.VISIBLE - } - } - .take(maxTasks) + .mapNotNull { taskId -> desktop.tasksByTaskIdMap[taskId] } .forEach { task -> - addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId) - addActiveTask(desktop.displayId, task.taskId) - updateTaskVisibility(desktop.displayId, task.taskId, visible = false) + if (task.desktopTaskState == DesktopTaskState.VISIBLE + && visibleTasksCount < maxTasks + ) { + visibleTasksCount++ + addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId) + addActiveTask(desktop.displayId, task.taskId) + updateTaskVisibility(desktop.displayId, task.taskId, visible = false) + } else { + addActiveTask(desktop.displayId, task.taskId) + updateTaskVisibility(desktop.displayId, task.taskId, visible = false) + minimizeTask(desktop.displayId, task.taskId) + } } } } @@ -260,11 +265,11 @@ class DesktopRepository ( ArraySet(desktopTaskDataByDisplayId[displayId]?.minimizedTasks) /** Returns all active non-minimized tasks for [displayId] ordered from top to bottom. */ - fun getActiveNonMinimizedOrderedTasks(displayId: Int): List<Int> = + fun getExpandedTasksOrdered(displayId: Int): List<Int> = getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) } /** Returns the count of active non-minimized tasks for [displayId]. */ - fun getActiveNonMinimizedTaskCount(displayId: Int): Int { + fun getExpandedTaskCount(displayId: Int): Int { return getActiveTasks(displayId).count { !isMinimizedTask(it) } } 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 69776cd4740a..bc78e43a15ad 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 @@ -48,6 +48,7 @@ import android.view.DragEvent import android.view.KeyEvent import android.view.MotionEvent import android.view.SurfaceControl +import android.view.SurfaceControl.Transaction import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_CLOSE import android.view.WindowManager.TRANSIT_NONE @@ -59,6 +60,7 @@ import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVI import android.window.DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS import android.window.RemoteTransition import android.window.TransitionInfo +import android.window.TransitionInfo.Change import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction import androidx.annotation.BinderThread @@ -115,6 +117,7 @@ import com.android.wm.shell.sysui.UserChangeListener import com.android.wm.shell.transition.FocusTransitionObserver import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.transition.Transitions.TransitionFinishCallback import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility import com.android.wm.shell.windowdecor.MoveToDesktopAnimator import com.android.wm.shell.windowdecor.OnTaskRepositionAnimationListener @@ -146,7 +149,7 @@ class DesktopTasksController( private val desktopModeDragAndDropTransitionHandler: DesktopModeDragAndDropTransitionHandler, private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler, private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler, - private val immersiveTransitionHandler: DesktopFullImmersiveTransitionHandler, + private val desktopImmersiveController: DesktopImmersiveController, private val taskRepository: DesktopRepository, private val desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver, private val launchAdjacentController: LaunchAdjacentController, @@ -252,7 +255,7 @@ class DesktopTasksController( toggleResizeDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener) enterDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener) dragToDesktopTransitionHandler.onTaskResizeAnimationListener = listener - immersiveTransitionHandler.onTaskResizeAnimationListener = listener + desktopImmersiveController.onTaskResizeAnimationListener = listener } fun setOnTaskRepositionAnimationListener(listener: OnTaskRepositionAnimationListener) { @@ -370,10 +373,13 @@ class DesktopTasksController( } logV("moveBackgroundTaskToDesktop with taskId=%d", taskId) // TODO(342378842): Instead of using default display, support multiple displays - val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( + val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( DEFAULT_DISPLAY, wct, taskId) - val runOnTransit = immersiveTransitionHandler - .exitImmersiveIfApplicable(wct, DEFAULT_DISPLAY) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + wct = wct, + displayId = DEFAULT_DISPLAY, + excludeTaskId = taskId, + ) wct.startTask( taskId, ActivityOptions.makeBasic().apply { @@ -382,7 +388,7 @@ class DesktopTasksController( ) // TODO(343149901): Add DPI changes for task launch val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource) - addPendingMinimizeTransition(transition, taskToMinimize) + taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } runOnTransit?.invoke(transition) return true } @@ -400,14 +406,18 @@ class DesktopTasksController( } logV("moveRunningTaskToDesktop taskId=%d", task.taskId) exitSplitIfApplicable(wct, task) - val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(wct, task.displayId) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + wct = wct, + displayId = task.displayId, + excludeTaskId = task.taskId, + ) // Bring other apps to front first - val taskToMinimize = + val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId) addMoveToDesktopChanges(wct, task) val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource) - addPendingMinimizeTransition(transition, taskToMinimize) + taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } runOnTransit?.invoke(transition) } @@ -442,14 +452,14 @@ class DesktopTasksController( val wct = WindowContainerTransaction() exitSplitIfApplicable(wct, taskInfo) moveHomeTask(wct, toTop = true) - val taskToMinimize = + val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId) addMoveToDesktopChanges(wct, taskInfo) - val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable( + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( wct, taskInfo.displayId) val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct) transition?.let { - addPendingMinimizeTransition(it, taskToMinimize) + taskIdToMinimize?.let { taskId -> addPendingMinimizeTransition(it, taskId) } runOnTransit?.invoke(transition) } } @@ -492,7 +502,7 @@ class DesktopTasksController( taskId ) ) - return immersiveTransitionHandler.exitImmersiveIfApplicable(wct, taskInfo) + return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo) } fun minimizeTask(taskInfo: RunningTaskInfo) { @@ -505,7 +515,7 @@ class DesktopTasksController( removeWallpaperActivity(wct) } // Notify immersive handler as it might need to exit immersive state. - val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(wct, taskInfo) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo) wct.reorder(taskInfo.token, false) val transition = freeformTaskTransitionStarter.startMinimizedModeTransition(wct) @@ -609,8 +619,11 @@ class DesktopTasksController( logV("moveBackgroundTaskToFront taskId=%s", taskId) val wct = WindowContainerTransaction() // TODO: b/342378842 - Instead of using default display, support multiple displays - val runOnTransit = immersiveTransitionHandler - .exitImmersiveIfApplicable(wct, DEFAULT_DISPLAY) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + wct = wct, + displayId = DEFAULT_DISPLAY, + excludeTaskId = taskId, + ) wct.startTask( taskId, ActivityOptions.makeBasic().apply { @@ -632,10 +645,19 @@ class DesktopTasksController( logV("moveTaskToFront taskId=%s", taskInfo.taskId) val wct = WindowContainerTransaction() wct.reorder(taskInfo.token, true /* onTop */, true /* includingParents */) - val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable( - wct, taskInfo.displayId) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + wct = wct, + displayId = taskInfo.displayId, + excludeTaskId = taskInfo.taskId, + ) val transition = - startLaunchTransition(TRANSIT_TO_FRONT, wct, taskInfo.taskId, remoteTransition) + startLaunchTransition( + TRANSIT_TO_FRONT, + wct, + taskInfo.taskId, + remoteTransition, + taskInfo.displayId + ) runOnTransit?.invoke(transition) } @@ -644,15 +666,15 @@ class DesktopTasksController( wct: WindowContainerTransaction, taskId: Int, remoteTransition: RemoteTransition?, + displayId: Int = DEFAULT_DISPLAY, ): IBinder { - val taskToMinimize: RunningTaskInfo? = - addAndGetMinimizeChangesIfNeeded(DEFAULT_DISPLAY, wct, taskId) + val taskIdToMinimize = addAndGetMinimizeChanges(displayId, wct, taskId) if (remoteTransition == null) { val t = transitions.startTransition(transitionType, wct, null /* handler */) - addPendingMinimizeTransition(t, taskToMinimize) + taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) } return t } - if (taskToMinimize == null) { + if (taskIdToMinimize == null) { val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition) val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler) remoteTransitionHandler.setTransition(t) @@ -660,10 +682,10 @@ class DesktopTasksController( } val remoteTransitionHandler = DesktopWindowLimitRemoteHandler( - mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskToMinimize.taskId) + mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskIdToMinimize) val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler) remoteTransitionHandler.setTransition(t) - addPendingMinimizeTransition(t, taskToMinimize) + taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) } return t } @@ -736,12 +758,12 @@ class DesktopTasksController( private fun moveDesktopTaskToFullImmersive(taskInfo: RunningTaskInfo) { check(taskInfo.isFreeform) { "Task must already be in freeform" } - immersiveTransitionHandler.moveTaskToImmersive(taskInfo) + desktopImmersiveController.moveTaskToImmersive(taskInfo) } private fun exitDesktopTaskFromFullImmersive(taskInfo: RunningTaskInfo) { check(taskInfo.isFreeform) { "Task must already be in freeform" } - immersiveTransitionHandler.moveTaskToNonImmersive(taskInfo) + desktopImmersiveController.moveTaskToNonImmersive(taskInfo) } /** @@ -856,7 +878,7 @@ class DesktopTasksController( excludeTaskId: Int? = null, ): Boolean { val doesAnyTaskRequireTaskbarRounding = - taskRepository.getActiveNonMinimizedOrderedTasks(displayId) + taskRepository.getExpandedTasksOrdered(displayId) // exclude current task since maximize/restore transition has not taken place yet. .filterNot { taskId -> taskId == excludeTaskId } .any { taskId -> @@ -1016,13 +1038,13 @@ class DesktopTasksController( displayId: Int, wct: WindowContainerTransaction, newTaskIdInFront: Int - ): RunningTaskInfo? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront) + ): Int? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront) private fun bringDesktopAppsToFront( displayId: Int, wct: WindowContainerTransaction, newTaskIdInFront: Int? = null - ): RunningTaskInfo? { + ): Int? { logV("bringDesktopAppsToFront, newTaskId=%d", newTaskIdInFront) // Move home to front, ensures that we go back home when all desktop windows are closed moveHomeTask(wct, toTop = true) @@ -1034,25 +1056,24 @@ class DesktopTasksController( addWallpaperActivity(wct) } - val nonMinimizedTasksOrderedFrontToBack = - taskRepository.getActiveNonMinimizedOrderedTasks(displayId) + val expandedTasksOrderedFrontToBack = + taskRepository.getExpandedTasksOrdered(displayId) // If we're adding a new Task we might need to minimize an old one // TODO(b/365725441): Handle non running task minimization - val taskToMinimize: RunningTaskInfo? = + val taskIdToMinimize: Int? = if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) { - desktopTasksLimiter - .get() - .getTaskToMinimizeIfNeeded( - nonMinimizedTasksOrderedFrontToBack, + desktopTasksLimiter.get() + .getTaskIdToMinimize( + expandedTasksOrderedFrontToBack, newTaskIdInFront ) } else { null } - nonMinimizedTasksOrderedFrontToBack + expandedTasksOrderedFrontToBack // If there is a Task to minimize, let it stay behind the Home Task - .filter { taskId -> taskId != taskToMinimize?.taskId } + .filter { taskId -> taskId != taskIdToMinimize } .reversed() // Start from the back so the front task is brought forward last .forEach { taskId -> val runningTaskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId) @@ -1073,7 +1094,7 @@ class DesktopTasksController( taskbarDesktopTaskListener?. onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId)) - return taskToMinimize + return taskIdToMinimize } private fun moveHomeTask(wct: WindowContainerTransaction, toTop: Boolean) { @@ -1220,6 +1241,67 @@ class DesktopTasksController( return result } + /** Whether the given [change] in the [transition] is a known desktop change. */ + fun isDesktopChange( + transition: IBinder, + change: TransitionInfo.Change, + ): Boolean { + // Only the immersive controller is currently involved in mixed transitions. + return Flags.enableFullyImmersiveInDesktop() + && desktopImmersiveController.isImmersiveChange(transition, change) + } + + /** + * Whether the given transition [info] will potentially include a desktop change, in which + * case the transition should be treated as mixed so that the change is in part animated by + * one of the desktop transition handlers. + */ + fun shouldPlayDesktopAnimation(info: TransitionRequestInfo): Boolean { + // Only immersive mixed transition are currently supported. + if (!Flags.enableFullyImmersiveInDesktop()) return false + val triggerTask = info.triggerTask ?: return false + if (!isDesktopModeShowing(triggerTask.displayId)) { + return false + } + if (!TransitionUtil.isOpeningType(info.type)) { + return false + } + taskRepository.getTaskInFullImmersiveState(displayId = triggerTask.displayId) + ?: return false + return when { + triggerTask.isFullscreen -> { + // Trigger fullscreen task will enter desktop, so any existing immersive task + // should exit. + shouldFullscreenTaskLaunchSwitchToDesktop(triggerTask) + } + triggerTask.isFreeform -> { + // Trigger freeform task will enter desktop, so any existing immersive task should + // exit. + !shouldFreeformTaskLaunchSwitchToFullscreen(triggerTask) + } + else -> false + } + } + + /** Animate a desktop change found in a mixed transitions. */ + fun animateDesktopChange( + transition: IBinder, + change: Change, + startTransaction: Transaction, + finishTransaction: Transaction, + finishCallback: TransitionFinishCallback, + ) { + if (!desktopImmersiveController.isImmersiveChange(transition, change)) { + throw IllegalStateException("Only immersive changes support desktop mixed transitions") + } + desktopImmersiveController.animateResizeChange( + change, + startTransaction, + finishTransaction, + finishCallback + ) + } + private fun taskContainsDragAndDropCookie(taskInfo: RunningTaskInfo?) = taskInfo?.launchCookies?.any { it == dragAndDropFullscreenCookie } ?: false @@ -1264,12 +1346,15 @@ class DesktopTasksController( val options = createNewWindowOptions(callingTask) if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) { wct.startTask(requestedTaskId, options.toBundle()) - val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( + val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( callingTask.displayId, wct, requestedTaskId) - val runOnTransit = immersiveTransitionHandler - .exitImmersiveIfApplicable(wct, callingTask.displayId) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + wct = wct, + displayId = callingTask.displayId, + excludeTaskId = requestedTaskId, + ) val transition = transitions.startTransition(TRANSIT_OPEN, wct, null) - addPendingMinimizeTransition(transition, taskToMinimize) + taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } runOnTransit?.invoke(transition) } else { val splitPosition = splitScreenController.determineNewInstancePosition(callingTask) @@ -1376,7 +1461,7 @@ class DesktopTasksController( return null } val wct = WindowContainerTransaction() - if (!isDesktopModeShowing(task.displayId)) { + if (shouldFreeformTaskLaunchSwitchToFullscreen(task)) { logD("Bring desktop tasks to front on transition=taskId=%d", task.taskId) if (taskRepository.isActiveTask(task.taskId) && !forceEnterDesktop(task.displayId)) { // We are outside of desktop mode and already existing desktop task is being @@ -1407,11 +1492,11 @@ class DesktopTasksController( } // Desktop Mode is showing and we're launching a new Task: // 1) Exit immersive if needed. - immersiveTransitionHandler.exitImmersiveIfApplicable(transition, wct, task.displayId) + desktopImmersiveController.exitImmersiveIfApplicable(transition, wct, task.displayId) // 2) minimize a Task if needed. - val taskToMinimize = addAndGetMinimizeChangesIfNeeded(task.displayId, wct, task.taskId) - if (taskToMinimize != null) { - addPendingMinimizeTransition(transition, taskToMinimize) + val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId) + if (taskIdToMinimize != null) { + addPendingMinimizeTransition(transition, taskIdToMinimize) return wct } return if (wct.isEmpty) null else wct @@ -1422,7 +1507,7 @@ class DesktopTasksController( transition: IBinder ): WindowContainerTransaction? { logV("handleFullscreenTaskLaunch") - if (isDesktopModeShowing(task.displayId) || forceEnterDesktop(task.displayId)) { + if (shouldFullscreenTaskLaunchSwitchToDesktop(task)) { logD("Switch fullscreen task to freeform on transition: taskId=%d", task.taskId) return WindowContainerTransaction().also { wct -> addMoveToDesktopChanges(wct, task) @@ -1435,10 +1520,9 @@ class DesktopTasksController( // 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.taskId) - addPendingMinimizeTransition(transition, taskToMinimize) - immersiveTransitionHandler.exitImmersiveIfApplicable( + val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId) + taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } + desktopImmersiveController.exitImmersiveIfApplicable( transition, wct, task.displayId ) } @@ -1446,6 +1530,12 @@ class DesktopTasksController( return null } + private fun shouldFreeformTaskLaunchSwitchToFullscreen(task: RunningTaskInfo): Boolean = + !isDesktopModeShowing(task.displayId) + + private fun shouldFullscreenTaskLaunchSwitchToDesktop(task: RunningTaskInfo): Boolean = + isDesktopModeShowing(task.displayId) || forceEnterDesktop(task.displayId) + /** * If a task is not compatible with desktop mode freeform, it should always be launched in * fullscreen. @@ -1564,7 +1654,7 @@ class DesktopTasksController( val stableBounds = Rect() displayLayout.getStableBoundsForDesktopMode(stableBounds) - val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(displayId) + val activeTasks = taskRepository.getExpandedTasksOrdered(displayId) activeTasks.firstOrNull()?.let { activeTask -> shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let { cascadeWindow(context.resources, stableBounds, @@ -1593,22 +1683,22 @@ class DesktopTasksController( } /** Returns the ID of the Task that will be minimized, or null if no task will be minimized. */ - private fun addAndGetMinimizeChangesIfNeeded( + private fun addAndGetMinimizeChanges( displayId: Int, wct: WindowContainerTransaction, newTaskId: Int - ): RunningTaskInfo? { + ): Int? { if (!desktopTasksLimiter.isPresent) return null return desktopTasksLimiter .get() - .addAndGetMinimizeTaskChangesIfNeeded(displayId, wct, newTaskId) + .addAndGetMinimizeTaskChanges(displayId, wct, newTaskId) } private fun addPendingMinimizeTransition( transition: IBinder, - taskToMinimize: RunningTaskInfo? + taskIdToMinimize: Int, ) { - if (taskToMinimize == null) return + val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) ?: return desktopTasksLimiter.ifPresent { it.addPendingMinimizeChange(transition, taskToMinimize.displayId, taskToMinimize.taskId) } 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 index d6b721253abf..7bcc5d1691aa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.desktopmode -import android.app.ActivityManager.RunningTaskInfo import android.content.Context import android.os.Handler import android.os.IBinder @@ -30,7 +29,7 @@ import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW import com.android.internal.jank.InteractionJankMonitor import com.android.internal.protolog.ProtoLog import com.android.wm.shell.ShellTaskOrganizer -import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.TransitionObserver @@ -57,13 +56,11 @@ class DesktopTasksLimiter ( init { require(maxTasksLimit > 0) { - "DesktopTasksLimiter should not be created with a maxTasksLimit at 0 or less. " + - "Current value: $maxTasksLimit." + "DesktopTasksLimiter: maxTasksLimit should be greater than 0. Current value: $maxTasksLimit." } transitions.registerObserver(minimizeTransitionObserver) taskRepository.addActiveTaskListener(leftoverMinimizedTasksRemover) - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: starting limiter with a maximum of %d tasks", maxTasksLimit) + logV("Starting limiter with a maximum of %d tasks", maxTasksLimit) } private data class TaskDetails( @@ -88,20 +85,14 @@ class DesktopTasksLimiter ( finishTransaction: SurfaceControl.Transaction ) { val taskToMinimize = pendingTransitionTokensAndTasks.remove(transition) ?: return - if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return - - if (!isTaskReorderedToBackOrInvisible(info, taskToMinimize)) { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: task %d is not reordered to back nor invis", - taskToMinimize.taskId) + if (!isTaskReadyForMinimize(info, taskToMinimize)) { + logV("task %d is not reordered to back nor invis", taskToMinimize.taskId) return } - taskToMinimize.transitionInfo = info activeTransitionTokensAndTasks[transition] = taskToMinimize - this@DesktopTasksLimiter.markTaskMinimized( + this@DesktopTasksLimiter.minimizeTask( taskToMinimize.displayId, taskToMinimize.taskId) } @@ -109,18 +100,15 @@ class DesktopTasksLimiter ( * Returns whether the Task [taskDetails] is being reordered to the back in the transition * [info], or is already invisible. * - * This check can be used to double-check that a task was indeed minimized before - * marking it as such. + * This check confirms a task should be minimized before minimizing it. */ - private fun isTaskReorderedToBackOrInvisible( - info: TransitionInfo, - taskDetails: TaskDetails + private fun isTaskReadyForMinimize( + info: TransitionInfo, + taskDetails: TaskDetails ): Boolean { val taskChange = info.changes.find { change -> change.taskInfo?.taskId == taskDetails.taskId } - if (taskChange == null) { - return !taskRepository.isVisibleTask(taskDetails.taskId) - } + if (taskChange == null) return !taskRepository.isVisibleTask(taskDetails.taskId) return taskChange.mode == TRANSIT_TO_BACK } @@ -145,9 +133,7 @@ class DesktopTasksLimiter ( } override fun onTransitionFinished(transition: IBinder, aborted: Boolean) { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: transition %s finished", transition) + logV("transition %s finished", transition) if (activeTransitionTokensAndTasks.remove(transition) != null) { if (aborted) { interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW) @@ -170,18 +156,11 @@ class DesktopTasksLimiter ( } fun removeLeftoverMinimizedTasks(displayId: Int, wct: WindowContainerTransaction) { - if (taskRepository.getActiveNonMinimizedOrderedTasks(displayId).isNotEmpty()) { - return - } + if (taskRepository.getExpandedTasksOrdered(displayId).isNotEmpty()) return val remainingMinimizedTasks = taskRepository.getMinimizedTasks(displayId) - if (remainingMinimizedTasks.isEmpty()) { - return - } - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: removing leftover minimized tasks: %s", - remainingMinimizedTasks, - ) + if (remainingMinimizedTasks.isEmpty()) return + + logV("Removing leftover minimized tasks: %s", remainingMinimizedTasks) remainingMinimizedTasks.forEach { taskIdToRemove -> val taskToRemove = shellTaskOrganizer.getRunningTaskInfo(taskIdToRemove) if (taskToRemove != null) { @@ -192,44 +171,41 @@ class DesktopTasksLimiter ( } /** - * Mark [taskId], which must be on [displayId], as minimized, this should only be done after the - * corresponding transition has finished so we don't minimize the task if the transition fails. + * Mark task with [taskId] on [displayId] as minimized. + * + * This should be after the corresponding transition has finished so we don't + * minimize the task if the transition fails. */ - private fun markTaskMinimized(displayId: Int, taskId: Int) { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: marking %d as minimized", taskId) + private fun minimizeTask(displayId: Int, taskId: Int) { + logV("Minimize taskId=%d, displayId=%d", taskId, displayId) taskRepository.minimizeTask(displayId, taskId) } /** - * Add a minimize-transition to [wct] if adding [newFrontTaskInfo] brings us over the task + * Adds a minimize-transition to [wct] if adding [newFrontTaskInfo] crosses task * limit, returning the task to minimize. - * - * The task must be on [displayId]. */ - fun addAndGetMinimizeTaskChangesIfNeeded( + fun addAndGetMinimizeTaskChanges( displayId: Int, wct: WindowContainerTransaction, newFrontTaskId: Int, - ): RunningTaskInfo? { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: addMinimizeBackTaskChangesIfNeeded, newFrontTask=%d", - newFrontTaskId) - val newTaskListOrderedFrontToBack = createOrderedTaskListWithGivenTaskInFront( - taskRepository.getActiveNonMinimizedOrderedTasks(displayId), - newFrontTaskId) - val taskToMinimize = getTaskToMinimizeIfNeeded(newTaskListOrderedFrontToBack) - if (taskToMinimize != null) { - wct.reorder(taskToMinimize.token, false /* onTop */) - return taskToMinimize + ): Int? { + logV("addAndGetMinimizeTaskChanges, newFrontTask=%d", newFrontTaskId) + + val taskIdToMinimize = + getTaskIdToMinimize( + taskRepository.getExpandedTasksOrdered(displayId), + newFrontTaskId + ) + // If it's a running task, reorder it to back. + taskIdToMinimize?.let { shellTaskOrganizer.getRunningTaskInfo(it) }?.let { + wct.reorder(it.token, false /* onTop */) } - return null + return taskIdToMinimize } /** - * Add a pending minimize transition change, to update the list of minimized apps once the + * 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) { @@ -238,51 +214,49 @@ class DesktopTasksLimiter ( } /** - * 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. + * Returns the minimized task from the list of visible tasks ordered from front to back with + * the new task placed in front of other tasks. */ - fun getTaskToMinimizeIfNeeded( - visibleFreeformTaskIdsOrderedFrontToBack: List<Int>, - newTaskIdInFront: Int - ): RunningTaskInfo? { - return getTaskToMinimizeIfNeeded( - createOrderedTaskListWithGivenTaskInFront( - visibleFreeformTaskIdsOrderedFrontToBack, newTaskIdInFront)) + fun getTaskIdToMinimize( + visibleOrderedTasks: List<Int>, + newTaskIdInFront: Int? = null + ): Int? { + return getTaskIdToMinimize( + createOrderedTaskListWithGivenTaskInFront( + visibleOrderedTasks, 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 <= maxTasksLimit) { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: no need to minimize; tasks below limit") + private fun getTaskIdToMinimize(visibleOrderedTasks: List<Int>): Int? { + if (visibleOrderedTasks.size <= maxTasksLimit) { + logV("No need to minimize; tasks below limit") // No need to minimize anything return null } - val taskIdToMinimize = visibleFreeformTaskIdsOrderedFrontToBack.last() - val taskToMinimize = - shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) - if (taskToMinimize == null) { - ProtoLog.e( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: taskToMinimize(taskId = %d) == null", - taskIdToMinimize, - ) - return null - } - return taskToMinimize + return visibleOrderedTasks.last() } private fun createOrderedTaskListWithGivenTaskInFront( existingTaskIdsOrderedFrontToBack: List<Int>, - newTaskId: Int + newTaskId: Int? ): List<Int> { - return listOf(newTaskId) + + return if (newTaskId == null) existingTaskIdsOrderedFrontToBack + else listOf(newTaskId) + existingTaskIdsOrderedFrontToBack.filter { taskId -> taskId != newTaskId } } @VisibleForTesting fun getTransitionObserver(): TransitionObserver = minimizeTransitionObserver -}
\ No newline at end of file + + private fun logV(msg: String, vararg arguments: Any?) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + + private fun logE(msg: String, vararg arguments: Any?) { + ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + + private companion object { + const val TAG = "DesktopTasksLimiter" + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt index 334dc5aca19d..f21a124f0b8b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt @@ -325,10 +325,15 @@ class AppHandleEducationController( /** * Listens to the changes to [WindowingEducationProto#hasEducationViewedTimestampMillis()] in * datastore proto object. + * + * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this flow will always emit false. That means + * it will emit education has not been viewed yet always. */ private fun isEducationViewedFlow(): Flow<Boolean> = appHandleEducationDatastoreRepository.dataStoreFlow - .map { preferences -> preferences.hasEducationViewedTimestampMillis() } + .map { preferences -> + preferences.hasEducationViewedTimestampMillis() && !SHOULD_OVERRIDE_EDUCATION_CONDITIONS + } .distinctUntilChanged() /** @@ -352,5 +357,10 @@ class AppHandleEducationController( val APP_HANDLE_EDUCATION_TIMEOUT_MILLIS: Long get() = SystemProperties.getLong("persist.windowing_app_handle_education_timeout", 400L) + + val SHOULD_OVERRIDE_EDUCATION_CONDITIONS: Boolean + get() = + SystemProperties.getBoolean( + "persist.desktop_windowing_app_handle_education_override_conditions", false) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt index 15f4c249cf22..144370d76060 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt @@ -23,10 +23,12 @@ import android.os.SystemClock import android.provider.Settings.Secure import com.android.wm.shell.R import com.android.wm.shell.desktopmode.CaptionState +import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.SHOULD_OVERRIDE_EDUCATION_CONDITIONS import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto import java.time.Duration +@kotlinx.coroutines.ExperimentalCoroutinesApi /** Filters incoming app handle education triggers based on set conditions. */ class AppHandleEducationFilter( private val context: Context, @@ -35,9 +37,16 @@ class AppHandleEducationFilter( private val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager - /** Returns true if conditions to show app handle education are met, returns false otherwise. */ + /** + * Returns true if conditions to show app handle education are met, returns false otherwise. + * + * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this method will always return + * ![captionState.isHandleMenuExpanded]. + */ suspend fun shouldShowAppHandleEducation(captionState: CaptionState): Boolean { if ((captionState as CaptionState.AppHandle).isHandleMenuExpanded) return false + if (SHOULD_OVERRIDE_EDUCATION_CONDITIONS) return true + val focusAppPackageName = captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false val windowingEducationProto = appHandleEducationDatastoreRepository.windowingEducationProto() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java index 771573d48e45..7631ece761b5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java @@ -28,7 +28,7 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.window.flags.Flags; -import com.android.wm.shell.desktopmode.DesktopFullImmersiveTransitionHandler; +import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.Transitions; @@ -48,7 +48,7 @@ import java.util.Optional; */ public class FreeformTaskTransitionObserver implements Transitions.TransitionObserver { private final Transitions mTransitions; - private final Optional<DesktopFullImmersiveTransitionHandler> mImmersiveTransitionHandler; + private final Optional<DesktopImmersiveController> mDesktopImmersiveController; private final WindowDecorViewModel mWindowDecorViewModel; private final Optional<TaskChangeListener> mTaskChangeListener; private final FocusTransitionObserver mFocusTransitionObserver; @@ -60,12 +60,12 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs Context context, ShellInit shellInit, Transitions transitions, - Optional<DesktopFullImmersiveTransitionHandler> immersiveTransitionHandler, + Optional<DesktopImmersiveController> desktopImmersiveController, WindowDecorViewModel windowDecorViewModel, Optional<TaskChangeListener> taskChangeListener, FocusTransitionObserver focusTransitionObserver) { mTransitions = transitions; - mImmersiveTransitionHandler = immersiveTransitionHandler; + mDesktopImmersiveController = desktopImmersiveController; mWindowDecorViewModel = windowDecorViewModel; mTaskChangeListener = taskChangeListener; mFocusTransitionObserver = focusTransitionObserver; @@ -89,7 +89,8 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs // TODO(b/367268953): Remove when DesktopTaskListener is introduced and the repository // is updated from there **before** the |mWindowDecorViewModel| methods are invoked. // Otherwise window decoration relayout won't run with the immersive state up to date. - mImmersiveTransitionHandler.ifPresent(h -> h.onTransitionReady(transition, info)); + mDesktopImmersiveController.ifPresent(h -> + h.onTransitionReady(transition, info, startT, finishT)); } // Update focus state first to ensure the correct state can be queried from listeners. // TODO(371503964): Remove this once the unified task repository is ready. @@ -194,10 +195,20 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs } @Override - public void onTransitionStarting(@NonNull IBinder transition) {} + public void onTransitionStarting(@NonNull IBinder transition) { + if (Flags.enableFullyImmersiveInDesktop()) { + // TODO(b/367268953): Remove when DesktopTaskListener is introduced. + mDesktopImmersiveController.ifPresent(h -> h.onTransitionStarting(transition)); + } + } @Override public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) { + if (Flags.enableFullyImmersiveInDesktop()) { + // TODO(b/367268953): Remove when DesktopTaskListener is introduced. + mDesktopImmersiveController.ifPresent(h -> h.onTransitionMerged(merged, playing)); + } + final List<ActivityManager.RunningTaskInfo> infoOfMerged = mTransitionToTaskInfo.get(merged); if (infoOfMerged == null) { @@ -218,6 +229,11 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs @Override public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) { + if (Flags.enableFullyImmersiveInDesktop()) { + // TODO(b/367268953): Remove when DesktopTaskListener is introduced. + mDesktopImmersiveController.ifPresent(h -> h.onTransitionFinished(transition, aborted)); + } + final List<ActivityManager.RunningTaskInfo> taskInfo = mTransitionToTaskInfo.getOrDefault(transition, Collections.emptyList()); mTransitionToTaskInfo.remove(transition); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 89d3dd63a08e..9e9de10d4b1c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -15,7 +15,12 @@ */ package com.android.wm.shell.pip.phone; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT; import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP; +import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE; import android.annotation.Nullable; import android.content.Context; @@ -23,6 +28,7 @@ import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.Region; import android.hardware.input.InputManager; import android.os.Looper; import android.view.BatchedInputEventReceiver; @@ -36,6 +42,7 @@ import android.view.ViewConfiguration; import androidx.annotation.VisibleForTesting; +import com.android.internal.policy.TaskResizingAlgorithm; import com.android.wm.shell.R; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; @@ -48,6 +55,7 @@ import com.android.wm.shell.pip.PipTaskOrganizer; import java.io.PrintWriter; import java.util.function.Consumer; +import java.util.function.Function; /** * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to @@ -71,6 +79,7 @@ public class PipResizeGestureHandler { private final PipPinchResizingAlgorithm mPinchResizingAlgorithm; private final int mDisplayId; private final ShellExecutor mMainExecutor; + private final Region mTmpRegion = new Region(); private final PointF mDownPoint = new PointF(); private final PointF mDownSecondPoint = new PointF(); @@ -81,15 +90,24 @@ public class PipResizeGestureHandler { private final Rect mLastResizeBounds = new Rect(); private final Rect mUserResizeBounds = new Rect(); private final Rect mDownBounds = new Rect(); + private final Rect mDragCornerSize = new Rect(); + private final Rect mTmpTopLeftCorner = new Rect(); + private final Rect mTmpTopRightCorner = new Rect(); + private final Rect mTmpBottomLeftCorner = new Rect(); + private final Rect mTmpBottomRightCorner = new Rect(); + private final Rect mDisplayBounds = new Rect(); + private final Function<Rect, Rect> mMovementBoundsSupplier; private final Runnable mUpdateMovementBoundsRunnable; private final Consumer<Rect> mUpdateResizeBoundsCallback; + private int mDelta; private float mTouchSlop; private boolean mAllowGesture; private boolean mIsAttached; private boolean mIsEnabled; private boolean mEnablePinchResize; + private boolean mEnableDragCornerResize; private boolean mIsSysUiStateValid; private boolean mThresholdCrossed; private boolean mOngoingPinchToResize = false; @@ -113,7 +131,7 @@ public class PipResizeGestureHandler { PipBoundsState pipBoundsState, PipMotionHelper motionHelper, PipTouchState pipTouchState, PipTaskOrganizer pipTaskOrganizer, PipDismissTargetHandler pipDismissTargetHandler, - Runnable updateMovementBoundsRunnable, + Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable, PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController, ShellExecutor mainExecutor, @Nullable PipPerfHintController pipPerfHintController) { mContext = context; @@ -126,6 +144,7 @@ public class PipResizeGestureHandler { mPipTouchState = pipTouchState; mPipTaskOrganizer = pipTaskOrganizer; mPipDismissTargetHandler = pipDismissTargetHandler; + mMovementBoundsSupplier = movementBoundsSupplier; mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; mPhonePipMenuController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; @@ -161,9 +180,20 @@ public class PipResizeGestureHandler { } private void reloadResources() { + final Resources res = mContext.getResources(); + mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size); + mEnableDragCornerResize = res.getBoolean(R.bool.config_pipEnableDragCornerResize); mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); } + private void resetDragCorners() { + mDragCornerSize.set(0, 0, mDelta, mDelta); + mTmpTopLeftCorner.set(mDragCornerSize); + mTmpTopRightCorner.set(mDragCornerSize); + mTmpBottomLeftCorner.set(mDragCornerSize); + mTmpBottomRightCorner.set(mDragCornerSize); + } + private void disposeInputChannel() { if (mInputEventReceiver != null) { mInputEventReceiver.dispose(); @@ -211,7 +241,7 @@ public class PipResizeGestureHandler { @VisibleForTesting void onInputEvent(InputEvent ev) { - if (!mEnablePinchResize) { + if (!mEnableDragCornerResize && !mEnablePinchResize) { // No need to handle anything if neither form of resizing is enabled. return; } @@ -239,6 +269,8 @@ public class PipResizeGestureHandler { if (mEnablePinchResize && mOngoingPinchToResize) { onPinchResize(mv); + } else if (mEnableDragCornerResize) { + onDragCornerResize(mv); } } } @@ -250,6 +282,48 @@ public class PipResizeGestureHandler { return mCtrlType != CTRL_NONE || mOngoingPinchToResize; } + /** + * Check whether the current x,y coordinate is within the region in which drag-resize should + * start. + * This consists of 4 small squares on the 4 corners of the PIP window, a quarter of which + * overlaps with the PIP window while the rest goes outside of the PIP window. + * _ _ _ _ + * |_|_|_________|_|_| + * |_|_| |_|_| + * | PIP | + * | WINDOW | + * _|_ _|_ + * |_|_|_________|_|_| + * |_|_| |_|_| + */ + public boolean isWithinDragResizeRegion(int x, int y) { + if (!mEnableDragCornerResize) { + return false; + } + + final Rect currentPipBounds = mPipBoundsState.getBounds(); + if (currentPipBounds == null) { + return false; + } + resetDragCorners(); + mTmpTopLeftCorner.offset(currentPipBounds.left - mDelta / 2, + currentPipBounds.top - mDelta / 2); + mTmpTopRightCorner.offset(currentPipBounds.right - mDelta / 2, + currentPipBounds.top - mDelta / 2); + mTmpBottomLeftCorner.offset(currentPipBounds.left - mDelta / 2, + currentPipBounds.bottom - mDelta / 2); + mTmpBottomRightCorner.offset(currentPipBounds.right - mDelta / 2, + currentPipBounds.bottom - mDelta / 2); + + mTmpRegion.setEmpty(); + mTmpRegion.op(mTmpTopLeftCorner, Region.Op.UNION); + mTmpRegion.op(mTmpTopRightCorner, Region.Op.UNION); + mTmpRegion.op(mTmpBottomLeftCorner, Region.Op.UNION); + mTmpRegion.op(mTmpBottomRightCorner, Region.Op.UNION); + + return mTmpRegion.contains(x, y); + } + public boolean isUsingPinchToZoom() { return mEnablePinchResize; } @@ -260,17 +334,62 @@ public class PipResizeGestureHandler { public boolean willStartResizeGesture(MotionEvent ev) { if (isInValidSysUiState()) { - if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { - if (mEnablePinchResize && ev.getPointerCount() == 2) { - onPinchResize(ev); - mOngoingPinchToResize = mAllowGesture; - return mAllowGesture; - } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + if (isWithinDragResizeRegion((int) ev.getRawX(), (int) ev.getRawY())) { + return true; + } + break; + + case MotionEvent.ACTION_POINTER_DOWN: + if (mEnablePinchResize && ev.getPointerCount() == 2) { + onPinchResize(ev); + mOngoingPinchToResize = mAllowGesture; + return mAllowGesture; + } + break; + + default: + break; } } return false; } + private void setCtrlType(int x, int y) { + final Rect currentPipBounds = mPipBoundsState.getBounds(); + + Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds); + + mDisplayBounds.set(movementBounds.left, + movementBounds.top, + movementBounds.right + currentPipBounds.width(), + movementBounds.bottom + currentPipBounds.height()); + + if (mTmpTopLeftCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top + && currentPipBounds.left != mDisplayBounds.left) { + mCtrlType |= CTRL_LEFT; + mCtrlType |= CTRL_TOP; + } + if (mTmpTopRightCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top + && currentPipBounds.right != mDisplayBounds.right) { + mCtrlType |= CTRL_RIGHT; + mCtrlType |= CTRL_TOP; + } + if (mTmpBottomRightCorner.contains(x, y) + && currentPipBounds.bottom != mDisplayBounds.bottom + && currentPipBounds.right != mDisplayBounds.right) { + mCtrlType |= CTRL_RIGHT; + mCtrlType |= CTRL_BOTTOM; + } + if (mTmpBottomLeftCorner.contains(x, y) + && currentPipBounds.bottom != mDisplayBounds.bottom + && currentPipBounds.left != mDisplayBounds.left) { + mCtrlType |= CTRL_LEFT; + mCtrlType |= CTRL_BOTTOM; + } + } + private boolean isInValidSysUiState() { return mIsSysUiStateValid; } @@ -364,6 +483,59 @@ public class PipResizeGestureHandler { } } + private void onDragCornerResize(MotionEvent ev) { + int action = ev.getActionMasked(); + float x = ev.getX(); + float y = ev.getY() - mOhmOffset; + if (action == MotionEvent.ACTION_DOWN) { + mLastResizeBounds.setEmpty(); + mAllowGesture = isInValidSysUiState() && isWithinDragResizeRegion((int) x, (int) y); + if (mAllowGesture) { + setCtrlType((int) x, (int) y); + mDownPoint.set(x, y); + mDownBounds.set(mPipBoundsState.getBounds()); + } + } else if (mAllowGesture) { + switch (action) { + case MotionEvent.ACTION_POINTER_DOWN: + // We do not support multi touch for resizing via drag + mAllowGesture = false; + break; + case MotionEvent.ACTION_MOVE: + // Capture inputs + if (!mThresholdCrossed + && Math.hypot(x - mDownPoint.x, y - mDownPoint.y) > mTouchSlop) { + mThresholdCrossed = true; + // Reset the down to begin resizing from this point + mDownPoint.set(x, y); + mInputMonitor.pilferPointers(); + } + if (mThresholdCrossed) { + if (mPhonePipMenuController.isMenuVisible()) { + mPhonePipMenuController.hideMenu(ANIM_TYPE_NONE, + false /* resize */); + } + final Rect currentPipBounds = mPipBoundsState.getBounds(); + mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y, + mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, + mMinSize.y, mMaxSize, true, + mDownBounds.width() > mDownBounds.height())); + mPipBoundsAlgorithm.transformBoundsToAspectRatio(mLastResizeBounds, + mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */, + true /* useCurrentSize */); + mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds, + null); + mPipBoundsState.setHasUserResizedPip(true); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + finishResize(); + break; + } + } + } + private void snapToMovementBoundsEdge(Rect bounds, Rect movementBounds) { final int leftEdge = bounds.left; 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 9c4e723efc23..f4c2a33079ba 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 @@ -213,7 +213,7 @@ public class PipTouchHandler { mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState, mMotionHelper, mTouchState, pipTaskOrganizer, mPipDismissTargetHandler, - this::updateMovementBounds, pipUiEventLogger, + this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger, menuController, mainExecutor, mPipPerfHintController); mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState, mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index 0ed5079b7fba..8ac7f89d8f8a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -476,6 +476,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal } mPipTaskOrganizer.removePip(); mTvPipMenuController.closeMenu(); + mPipNotificationController.dismiss(); } @Override 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 f7ed1dd4606b..6d4d4b410be8 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 @@ -585,7 +585,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - "[%d] RecentsController.cancelSyntheticTransition reason=%s", + "[%d] RecentsController.cancelSyntheticTransition: reason=%s", mInstanceId, reason); try { // TODO(b/366021931): Notify the correct tasks once we build actual targets, and @@ -602,13 +602,24 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, * Called when a synthetic transition is finished. * @return */ - boolean finishSyntheticTransition() { + boolean finishSyntheticTransition(IResultReceiver runnerFinishCb, String reason) { if (!isSyntheticTransition()) { return false; } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - "[%d] RecentsController.finishSyntheticTransition", mInstanceId); + "[%d] RecentsController.finishSyntheticTransition: reason=%s", mInstanceId, + reason); + if (runnerFinishCb != null) { + try { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.finishInner: calling finish callback", + mInstanceId); + runnerFinishCb.send(0, null); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report transition finished", e); + } + } // TODO(b/366021931): Clean up leashes accordingly cleanUp(); return true; @@ -1230,7 +1241,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, private void finishInner(boolean toHome, boolean sendUserLeaveHint, IResultReceiver runnerFinishCb, String reason) { - if (finishSyntheticTransition()) { + if (finishSyntheticTransition(runnerFinishCb, reason)) { return; } 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 766a6b3f48ac..0d89f757903e 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 @@ -83,8 +83,11 @@ public class DefaultMixedHandler implements MixedTransitionHandler, /** Both the display and split-state (enter/exit) is changing */ static final int TYPE_DISPLAY_AND_SPLIT_CHANGE = 2; - /** Pip was entered while handling an intent with its own remoteTransition. */ - static final int TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE = 3; + /** + * While handling an intent with its own remoteTransition, a PIP enter or Desktop immersive + * exit change is found. + */ + static final int TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE = 3; /** Recents transition while split-screen foreground. */ static final int TYPE_RECENTS_DURING_SPLIT = 4; @@ -110,6 +113,9 @@ public class DefaultMixedHandler implements MixedTransitionHandler, /** The display changes when pip is entering. */ static final int TYPE_ENTER_PIP_WITH_DISPLAY_CHANGE = 11; + /** Open transition during a desktop session. */ + static final int TYPE_OPEN_IN_DESKTOP = 12; + /** The default animation for this mixed transition. */ static final int ANIM_TYPE_DEFAULT = 0; @@ -296,7 +302,7 @@ public class DefaultMixedHandler implements MixedTransitionHandler, return null; } final MixedTransition mixed = createDefaultMixedTransition( - MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE, transition); + MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE, transition); mixed.mLeftoversHandler = handler.first; mActiveTransitions.add(mixed); if (mixed.mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { @@ -334,6 +340,20 @@ public class DefaultMixedHandler implements MixedTransitionHandler, MixedTransition.TYPE_UNFOLD, transition)); } return wct; + } else if (mDesktopTasksController != null + && mDesktopTasksController.shouldPlayDesktopAnimation(request)) { + final Pair<Transitions.TransitionHandler, WindowContainerTransaction> handler = + mPlayer.dispatchRequest(transition, request, /* skip= */ this); + if (handler == null) { + return null; + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a desktop request, so" + + " treat it as Mixed. handler=%s", handler.first); + final MixedTransition mixed = createDefaultMixedTransition( + MixedTransition.TYPE_OPEN_IN_DESKTOP, transition); + mixed.mLeftoversHandler = handler.first; + mActiveTransitions.add(mixed); + return handler.second; } return null; } @@ -341,7 +361,7 @@ public class DefaultMixedHandler implements MixedTransitionHandler, private DefaultMixedTransition createDefaultMixedTransition(int type, IBinder transition) { return new DefaultMixedTransition( type, transition, mPlayer, this, mPipHandler, mSplitHandler, mKeyguardHandler, - mUnfoldHandler, mActivityEmbeddingController); + mUnfoldHandler, mActivityEmbeddingController, mDesktopTasksController); } @Override 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 c8921d256d7f..3d3de88cdafc 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 @@ -30,6 +30,7 @@ import android.window.TransitionInfo; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.activityembedding.ActivityEmbeddingController; +import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -39,15 +40,19 @@ import com.android.wm.shell.unfold.UnfoldTransitionHandler; class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { private final UnfoldTransitionHandler mUnfoldHandler; private final ActivityEmbeddingController mActivityEmbeddingController; + @Nullable + private final DesktopTasksController mDesktopTasksController; DefaultMixedTransition(int type, IBinder transition, Transitions player, MixedTransitionHandler mixedHandler, PipTransitionController pipHandler, StageCoordinator splitHandler, KeyguardTransitionHandler keyguardHandler, UnfoldTransitionHandler unfoldHandler, - ActivityEmbeddingController activityEmbeddingController) { + ActivityEmbeddingController activityEmbeddingController, + @Nullable DesktopTasksController desktopTasksController) { super(type, transition, player, mixedHandler, pipHandler, splitHandler, keyguardHandler); mUnfoldHandler = unfoldHandler; mActivityEmbeddingController = activityEmbeddingController; + mDesktopTasksController = desktopTasksController; switch (type) { case TYPE_UNFOLD: @@ -57,7 +62,8 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING: case TYPE_ENTER_PIP_FROM_SPLIT: case TYPE_KEYGUARD: - case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + case TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE: + case TYPE_OPEN_IN_DESKTOP: default: break; } @@ -85,11 +91,14 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { case TYPE_KEYGUARD -> animateKeyguard(this, info, startTransaction, finishTransaction, finishCallback, mKeyguardHandler, mPipHandler); - case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE -> - animateOpenIntentWithRemoteAndPip(transition, info, startTransaction, + case TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE -> + animateOpenIntentWithRemoteAndPipOrDesktop(transition, info, startTransaction, finishTransaction, finishCallback); case TYPE_UNFOLD -> animateUnfold(info, startTransaction, finishTransaction, finishCallback); + case TYPE_OPEN_IN_DESKTOP -> + animateOpenInDesktop( + transition, info, startTransaction, finishTransaction, finishCallback); default -> throw new IllegalStateException( "Starting default mixed animation with unknown or illegal type: " + mType); }; @@ -146,31 +155,34 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { return true; } - private boolean animateOpenIntentWithRemoteAndPip( + private boolean animateOpenIntentWithRemoteAndPipOrDesktop( @NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Mixed transition for opening an intent" - + " with a remote transition and PIP #%d", info.getDebugId()); - boolean handledToPip = tryAnimateOpenIntentWithRemoteAndPip( + + " with a remote transition and PIP or Desktop #%d", info.getDebugId()); + boolean handledToPipOrDesktop = tryAnimateOpenIntentWithRemoteAndPipOrDesktop( info, startTransaction, finishTransaction, finishCallback); // Consume the transition on remote handler if the leftover handler already handle this // transition. And if it cannot, the transition will be handled by remote handler, so don't // consume here. - // Need to check leftOverHandler as it may change in #animateOpenIntentWithRemoteAndPip - if (handledToPip && mHasRequestToRemote + // Need to check leftOverHandler as it may change in + // #animateOpenIntentWithRemoteAndPipOrDesktop + if (handledToPipOrDesktop && mHasRequestToRemote && mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, false, null); } - return handledToPip; + return handledToPipOrDesktop; } - private boolean tryAnimateOpenIntentWithRemoteAndPip( + private boolean tryAnimateOpenIntentWithRemoteAndPipOrDesktop( @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "tryAnimateOpenIntentWithRemoteAndPipOrDesktop"); TransitionInfo.Change pipChange = null; for (int i = info.getChanges().size() - 1; i >= 0; --i) { TransitionInfo.Change change = info.getChanges().get(i); @@ -183,13 +195,31 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { info.getChanges().remove(i); } } + TransitionInfo.Change desktopChange = null; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (mDesktopTasksController != null + && mDesktopTasksController.isDesktopChange(mTransition, change)) { + if (desktopChange != null) { + throw new IllegalStateException("More than 1 desktop changes in one" + + " transition? " + info); + } + desktopChange = change; + info.getChanges().remove(i); + } + } Transitions.TransitionFinishCallback finishCB = (wct) -> { --mInFlightSubAnimations; joinFinishArgs(wct); if (mInFlightSubAnimations > 0) return; finishCallback.onTransitionFinished(mFinishWCT); }; - if (pipChange == null) { + if ((pipChange == null && desktopChange == null) + || (pipChange != null && desktopChange != null)) { + // Don't split the transition. Let the leftovers handler handle it all. + // TODO: b/? - split the transition into three pieces when there's both a PIP and a + // desktop change are present. For example, during remote intent open over a desktop + // with both a PIP capable task and an immersive task. if (mLeftoversHandler != null) { mInFlightSubAnimations = 1; if (mLeftoversHandler.startAnimation( @@ -198,27 +228,52 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { } } return false; - } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Splitting PIP into a separate" - + " animation because remote-animation likely doesn't support it #%d", - info.getDebugId()); - // Split the transition into 2 parts: the pip part and the rest. - mInFlightSubAnimations = 2; - // make a new startTransaction because pip's startEnterAnimation "consumes" it so - // we need a separate one to send over to launcher. - SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); + } else if (pipChange != null && desktopChange == null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Splitting PIP into a separate" + + " animation because remote-animation likely doesn't support it #%d", + info.getDebugId()); + // Split the transition into 2 parts: the pip part and the rest. + mInFlightSubAnimations = 2; + // make a new startTransaction because pip's startEnterAnimation "consumes" it so + // we need a separate one to send over to launcher. + SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); + + mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB); + + // Dispatch the rest of the transition normally. + if (mLeftoversHandler != null + && mLeftoversHandler.startAnimation(mTransition, info, + startTransaction, finishTransaction, finishCB)) { + return true; + } + mLeftoversHandler = mPlayer.dispatchTransition( + mTransition, info, startTransaction, finishTransaction, finishCB, + mMixedHandler); + return true; + } else if (pipChange == null && desktopChange != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Splitting desktop change into a" + + "separate animation because remote-animation likely doesn't support" + + "it #%d", info.getDebugId()); + mInFlightSubAnimations = 2; + SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); - mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB); + mDesktopTasksController.animateDesktopChange( + mTransition, desktopChange, otherStartT, finishTransaction, finishCB); - // Dispatch the rest of the transition normally. - if (mLeftoversHandler != null - && mLeftoversHandler.startAnimation(mTransition, info, - startTransaction, finishTransaction, finishCB)) { + // Dispatch the rest of the transition normally. + if (mLeftoversHandler != null + && mLeftoversHandler.startAnimation(mTransition, info, + startTransaction, finishTransaction, finishCB)) { + return true; + } + mLeftoversHandler = mPlayer.dispatchTransition( + mTransition, info, startTransaction, finishTransaction, finishCB, + mMixedHandler); return true; + } else { + throw new IllegalStateException( + "All PIP and Immersive combinations should've been handled"); } - mLeftoversHandler = mPlayer.dispatchTransition( - mTransition, info, startTransaction, finishTransaction, finishCB, mMixedHandler); - return true; } private boolean animateUnfold( @@ -246,6 +301,51 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { mTransition, info, startTransaction, finishTransaction, finishCB); } + private boolean animateOpenInDesktop( + @NonNull IBinder transition, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "animateOpenInDesktop"); + TransitionInfo.Change desktopChange = null; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (mDesktopTasksController.isDesktopChange(mTransition, change)) { + if (desktopChange != null) { + throw new IllegalStateException("More than 1 desktop changes in one" + + " transition? " + info); + } + desktopChange = change; + info.getChanges().remove(i); + } + } + final Transitions.TransitionFinishCallback finishCB = (wct) -> { + --mInFlightSubAnimations; + joinFinishArgs(wct); + if (mInFlightSubAnimations > 0) return; + finishCallback.onTransitionFinished(mFinishWCT); + }; + if (desktopChange == null) { + if (mLeftoversHandler != null) { + mInFlightSubAnimations = 1; + if (mLeftoversHandler.startAnimation( + mTransition, info, startTransaction, finishTransaction, finishCB)) { + return true; + } + } + return false; + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Splitting desktop change into a" + + "separate animation #%d", info.getDebugId()); + mInFlightSubAnimations = 2; + mDesktopTasksController.animateDesktopChange( + transition, desktopChange, startTransaction, finishTransaction, finishCB); + mLeftoversHandler = mPlayer.dispatchTransition( + mTransition, info, startTransaction, finishTransaction, finishCB, mMixedHandler); + return true; + } + @Override void mergeAnimation( @NonNull IBinder transition, @NonNull TransitionInfo info, @@ -279,7 +379,7 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { case TYPE_KEYGUARD: mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); return; - case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + case TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE: mPipHandler.end(); if (mLeftoversHandler != null) { mLeftoversHandler.mergeAnimation( @@ -289,6 +389,10 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { case TYPE_UNFOLD: mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); return; + case TYPE_OPEN_IN_DESKTOP: + mDesktopTasksController.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + return; default: throw new IllegalStateException("Playing a default mixed transition with unknown or" + " illegal type: " + mType); @@ -310,12 +414,14 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { case TYPE_KEYGUARD: mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); break; - case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + case TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE: mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); break; case TYPE_UNFOLD: mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); break; + case TYPE_OPEN_IN_DESKTOP: + mDesktopTasksController.onTransitionConsumed(transition, aborted, finishT); default: break; } 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 346f21b86e65..7c9cd0862b69 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 @@ -18,6 +18,7 @@ package com.android.wm.shell.transition; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM; @@ -58,6 +59,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; +import android.os.Trace; import android.provider.Settings; import android.util.ArrayMap; import android.util.Log; @@ -800,8 +802,17 @@ public class Transitions implements RemoteCallable<Transitions>, track.mReadyTransitions.add(active); for (int i = 0; i < mObservers.size(); ++i) { + final boolean useTrace = Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER); + if (useTrace) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + mObservers.get(i).getClass().getSimpleName() + "#onTransitionReady: " + + transitTypeToString(info.getType())); + } mObservers.get(i).onTransitionReady( active.mToken, info, active.mStartT, active.mFinishT); + if (useTrace) { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } } /* @@ -931,7 +942,7 @@ public class Transitions implements RemoteCallable<Transitions>, onFinish(ready.mToken, null); return; } - playTransition(ready); + playTransitionWithTracing(ready); // Attempt to merge any more queued-up transitions. processReadyQueue(track); return; @@ -1003,6 +1014,18 @@ public class Transitions implements RemoteCallable<Transitions>, processReadyQueue(track); } + private void playTransitionWithTracing(@NonNull ActiveTransition active) { + final boolean useTrace = Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER); + if (useTrace) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "playTransition: " + transitTypeToString(active.mInfo.getType())); + } + playTransition(active); + if (useTrace) { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + } + private void playTransition(@NonNull ActiveTransition active) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active); final var token = active.mToken; @@ -1022,6 +1045,12 @@ public class Transitions implements RemoteCallable<Transitions>, if (consumed) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler"); mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler); + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + Trace.instant(TRACE_TAG_WINDOW_MANAGER, + active.mHandler.getClass().getSimpleName() + + "#startAnimation animated " + + transitTypeToString(active.mInfo.getType())); + } return; } } @@ -1052,6 +1081,12 @@ public class Transitions implements RemoteCallable<Transitions>, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s", mHandlers.get(i)); mTransitionTracer.logDispatched(info.getDebugId(), mHandlers.get(i)); + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + Trace.instant(TRACE_TAG_WINDOW_MANAGER, + mHandlers.get(i).getClass().getSimpleName() + + "#startAnimation animated " + + transitTypeToString(info.getType())); + } return mHandlers.get(i); } } @@ -1059,6 +1094,26 @@ public class Transitions implements RemoteCallable<Transitions>, "This shouldn't happen, maybe the default handler is broken."); } + private Pair<TransitionHandler, WindowContainerTransaction> dispatchRequestWithTracing( + @NonNull IBinder transition, @NonNull TransitionRequestInfo request, + @Nullable TransitionHandler skip) { + final boolean useTrace = Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER); + if (useTrace) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "dispatchRequest: " + transitTypeToString(request.getType())); + } + Pair<TransitionHandler, WindowContainerTransaction> result = + dispatchRequest(transition, request, skip); + if (useTrace) { + if (result != null) { + Trace.instant(TRACE_TAG_WINDOW_MANAGER, result.first.getClass().getSimpleName() + + "#handleRequest handled " + transitTypeToString(request.getType())); + } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + return result; + } + /** * Gives every handler (in order) a chance to handle request until one consumes the transition. * @return the WindowContainerTransaction given by the handler which consumed the transition. @@ -1197,12 +1252,11 @@ public class Transitions implements RemoteCallable<Transitions>, mSleepHandler.handleRequest(transitionToken, request); active.mHandler = mSleepHandler; } else { - for (int i = mHandlers.size() - 1; i >= 0; --i) { - wct = mHandlers.get(i).handleRequest(transitionToken, request); - if (wct != null) { - active.mHandler = mHandlers.get(i); - break; - } + Pair<TransitionHandler, WindowContainerTransaction> requestResult = + dispatchRequestWithTracing(transitionToken, request, /* skip= */ null); + if (requestResult != null) { + active.mHandler = requestResult.first; + wct = requestResult.second; } if (request.getDisplayChange() != null) { TransitionRequestInfo.DisplayChange change = request.getDisplayChange(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java index 60c922293d80..78e7962dcec3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java @@ -29,8 +29,6 @@ import android.util.DisplayMetrics; import android.view.SurfaceControl; import android.window.DesktopModeFlags; -import androidx.annotation.NonNull; - import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; @@ -129,13 +127,15 @@ public class DragPositioningCallbackUtility { // If width or height are negative or exceeding the width or height constraints, revert the // respective bounds to use previous bound dimensions. - if (isExceedingWidthConstraint(repositionTaskBounds, stableBounds, displayController, + if (isExceedingWidthConstraint(repositionTaskBounds.width(), + /* startingWidth= */ oldRight - oldLeft, stableBounds, displayController, windowDecoration)) { repositionTaskBounds.right = oldRight; repositionTaskBounds.left = oldLeft; isAspectRatioMaintained = false; } - if (isExceedingHeightConstraint(repositionTaskBounds, stableBounds, displayController, + if (isExceedingHeightConstraint(repositionTaskBounds.height(), + /* startingHeight= */oldBottom - oldTop, stableBounds, displayController, windowDecoration)) { repositionTaskBounds.top = oldTop; repositionTaskBounds.bottom = oldBottom; @@ -208,28 +208,34 @@ public class DragPositioningCallbackUtility { return result; } - private static boolean isExceedingWidthConstraint(@NonNull Rect repositionTaskBounds, + private static boolean isExceedingWidthConstraint(int repositionedWidth, int startingWidth, Rect maxResizeBounds, DisplayController displayController, WindowDecoration windowDecoration) { + boolean isSizeIncreasing = (repositionedWidth - startingWidth) > 0; // Check if width is less than the minimum width constraint. - if (repositionTaskBounds.width() < getMinWidth(displayController, windowDecoration)) { - return true; + if (repositionedWidth < getMinWidth(displayController, windowDecoration)) { + // Only allow width to be increased if it is already below minimum. + return !isSizeIncreasing; } // Check if width is more than the maximum resize bounds on desktop windowing mode. + // Only allow width to be decreased if it already exceeds maximum. return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) - && repositionTaskBounds.width() > maxResizeBounds.width(); + && repositionedWidth > maxResizeBounds.width() && isSizeIncreasing; } - private static boolean isExceedingHeightConstraint(@NonNull Rect repositionTaskBounds, + private static boolean isExceedingHeightConstraint(int repositionedHeight, int startingHeight, Rect maxResizeBounds, DisplayController displayController, WindowDecoration windowDecoration) { + boolean isSizeIncreasing = (repositionedHeight - startingHeight) > 0; // Check if height is less than the minimum height constraint. - if (repositionTaskBounds.height() < getMinHeight(displayController, windowDecoration)) { - return true; + if (repositionedHeight < getMinHeight(displayController, windowDecoration)) { + // Only allow height to be increased if it is already below minimum. + return !isSizeIncreasing; } // Check if height is more than the maximum resize bounds on desktop windowing mode. + // Only allow height to be decreased if it already exceeds maximum. return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) - && repositionTaskBounds.height() > maxResizeBounds.height(); + && repositionedHeight > maxResizeBounds.height() && isSizeIncreasing; } private static float getMinWidth(DisplayController displayController, 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 dee0b23a42f5..72d4dc6ffac9 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 @@ -51,6 +51,7 @@ import android.content.pm.ApplicationInfo; import android.graphics.Point; import android.graphics.Rect; import android.hardware.input.InputManager; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -776,6 +777,14 @@ public class BackAnimationControllerTest extends ShellTestCase { verify(mergeCallback, never()).onTransitionFinished(any()); } + @Test + public void testBackAnimationControllersRecoversFromBadState() throws RemoteException { + // put controller into bad state (initial state but mBackGestureStarted=true) + mController.mBackGestureStarted = true; + verifySystemBackBehavior(BackNavigationInfo.TYPE_CROSS_ACTIVITY, + mDefaultCrossActivityBackAnimation.getRunner()); + } + private RemoteAnimationTarget[] createAppAnimationTargets(int openTaskId, int closeTaskId) { final RemoteAnimationTarget openT = createSingleAnimationTarget(openTaskId, RemoteAnimationTarget.MODE_OPENING); @@ -804,7 +813,10 @@ public class BackAnimationControllerTest extends ShellTestCase { if (taskId != INVALID_TASK_ID) { final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); taskInfo.taskId = taskId; - taskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class)); + final IWindowContainerToken mockT = mock(IWindowContainerToken.class); + Binder binder = new Binder(); + doReturn(binder).when(mockT).asBinder(); + taskInfo.token = new WindowContainerToken(mockT); change = new TransitionInfo.Change( taskInfo.token, b.build()); change.setTaskInfo(taskInfo); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java index d6059a88e9c7..8fd7c0ec3099 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java @@ -16,6 +16,10 @@ package com.android.wm.shell.compatui; +import static com.android.wm.shell.compatui.CompatUIStatusManager.COMPAT_UI_EDUCATION_HIDDEN; + +import static junit.framework.Assert.assertEquals; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -25,7 +29,6 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -63,13 +66,75 @@ public class CompatUIStatusManagerTest extends ShellTestCase { assertFalse(mStatusManager.isEducationVisible()); } + @Test + public void valuesAreCached() { + // At the beginning the value is not read or written because + // we access the reader in lazy way. + mTestState.assertReaderInvocations(0); + mTestState.assertWriterInvocations(0); + + // We read the value when we start. Initial value is hidden. + assertFalse(mStatusManager.isEducationVisible()); + mTestState.assertReaderInvocations(1); + mTestState.assertWriterInvocations(0); + + // We send the event for the same state which is not written. + mStatusManager.onEducationHidden(); + assertFalse(mStatusManager.isEducationVisible()); + mTestState.assertReaderInvocations(1); + mTestState.assertWriterInvocations(0); + + // We send the event for the different state which is written but + // not read again. + mStatusManager.onEducationShown(); + assertTrue(mStatusManager.isEducationVisible()); + mTestState.assertReaderInvocations(1); + mTestState.assertWriterInvocations(1); + + // We read multiple times and we don't read the value again + mStatusManager.isEducationVisible(); + mStatusManager.isEducationVisible(); + mStatusManager.isEducationVisible(); + mTestState.assertReaderInvocations(1); + mTestState.assertWriterInvocations(1); + + // We write different values. Writer is only accessed when + // the value changes. + mStatusManager.onEducationHidden(); // change + mStatusManager.onEducationHidden(); + mStatusManager.onEducationShown(); // change + mStatusManager.onEducationShown(); + mStatusManager.onEducationHidden(); // change + mStatusManager.onEducationShown(); // change + mTestState.assertReaderInvocations(1); + mTestState.assertWriterInvocations(5); + } + static class FakeCompatUIStatusManagerTest { - int mCurrentStatus = 0; + int mCurrentStatus = COMPAT_UI_EDUCATION_HIDDEN; + + int mReaderInvocations; + + int mWriterInvocations; + + final IntSupplier mReader = () -> { + mReaderInvocations++; + return mCurrentStatus; + }; + + final IntConsumer mWriter = newStatus -> { + mWriterInvocations++; + mCurrentStatus = newStatus; + }; - final IntSupplier mReader = () -> mCurrentStatus; + void assertWriterInvocations(int expected) { + assertEquals(expected, mWriterInvocations); + } - final IntConsumer mWriter = newStatus -> mCurrentStatus = newStatus; + void assertReaderInvocations(int expected) { + assertEquals(expected, mReaderInvocations); + } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt index ef99b000d759..e83f5c7a79a1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt @@ -15,6 +15,7 @@ */ package com.android.wm.shell.desktopmode +import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS import android.graphics.Rect import android.os.Binder @@ -58,13 +59,13 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.whenever /** - * Tests for [DesktopFullImmersiveTransitionHandler]. + * Tests for [DesktopImmersiveController]. * - * Usage: atest WMShellUnitTests:DesktopFullImmersiveTransitionHandlerTest + * Usage: atest WMShellUnitTests:DesktopImmersiveControllerTest */ @SmallTest @RunWith(AndroidTestingRunner::class) -class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { +class DesktopImmersiveControllerTest : ShellTestCase() { @JvmField @Rule val setFlagsRule = SetFlagsRule() @@ -75,7 +76,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { @Mock private lateinit var mockDisplayLayout: DisplayLayout private val transactionSupplier = { SurfaceControl.Transaction() } - private lateinit var immersiveHandler: DesktopFullImmersiveTransitionHandler + private lateinit var controller: DesktopImmersiveController @Before fun setUp() { @@ -87,7 +88,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { invocation -> (invocation.getArgument(0) as Rect).set(STABLE_BOUNDS) } - immersiveHandler = DesktopFullImmersiveTransitionHandler( + controller = DesktopImmersiveController( transitions = mockTransitions, desktopRepository = desktopRepository, displayController = mockDisplayController, @@ -100,7 +101,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { fun enterImmersive_transitionReady_updatesRepository() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) desktopRepository.setTaskInFullImmersiveState( displayId = task.displayId, @@ -108,16 +109,14 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.moveTaskToImmersive(task) - immersiveHandler.onTransitionReady( + controller.moveTaskToImmersive(task) + controller.onTransitionReady( transition = mockBinder, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { - taskInfo = task - } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isTrue() @@ -128,7 +127,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { fun enterImmersive_savesPreImmersiveBounds() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) desktopRepository.setTaskInFullImmersiveState( displayId = task.displayId, @@ -137,16 +136,14 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { ) assertThat(desktopRepository.removeBoundsBeforeFullImmersive(task.taskId)).isNull() - immersiveHandler.moveTaskToImmersive(task) - immersiveHandler.onTransitionReady( + controller.moveTaskToImmersive(task) + controller.onTransitionReady( transition = mockBinder, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { - taskInfo = task - } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.removeBoundsBeforeFullImmersive(task.taskId)).isNotNull() @@ -156,7 +153,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { fun exitImmersive_transitionReady_updatesRepository() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) desktopRepository.setTaskInFullImmersiveState( displayId = task.displayId, @@ -164,16 +161,14 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.moveTaskToNonImmersive(task) - immersiveHandler.onTransitionReady( + controller.moveTaskToNonImmersive(task) + controller.onTransitionReady( transition = mockBinder, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { - taskInfo = task - } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse() @@ -184,7 +179,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { fun exitImmersive_onTransitionReady_removesBoundsBeforeImmersive() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) desktopRepository.setTaskInFullImmersiveState( displayId = task.displayId, @@ -193,16 +188,14 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { ) desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600)) - immersiveHandler.moveTaskToNonImmersive(task) - immersiveHandler.onTransitionReady( + controller.moveTaskToNonImmersive(task) + controller.onTransitionReady( transition = mockBinder, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { - taskInfo = task - } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.removeBoundsBeforeMaximize(task.taskId)).isNull() @@ -217,16 +210,15 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.onTransitionReady( + controller.onTransitionReady( transition = mock(IBinder::class.java), info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { - taskInfo = task - setRotation(/* start= */ Surface.ROTATION_0, /* end= */ Surface.ROTATION_90) - } - ) - ) + changes = listOf(createChange(task).apply { + setRotation(/* start= */ Surface.ROTATION_0, /* end= */ Surface.ROTATION_90) + }) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse() @@ -236,28 +228,28 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { fun enterImmersive_inProgress_ignores() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) - immersiveHandler.moveTaskToImmersive(task) - immersiveHandler.moveTaskToImmersive(task) + controller.moveTaskToImmersive(task) + controller.moveTaskToImmersive(task) verify(mockTransitions, times(1)) - .startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler)) + .startTransition(eq(TRANSIT_CHANGE), any(), eq(controller)) } @Test fun exitImmersive_inProgress_ignores() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) - immersiveHandler.moveTaskToNonImmersive(task) - immersiveHandler.moveTaskToNonImmersive(task) + controller.moveTaskToNonImmersive(task) + controller.moveTaskToNonImmersive(task) verify(mockTransitions, times(1)) - .startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler)) + .startTransition(eq(TRANSIT_CHANGE), any(), eq(controller)) } @Test @@ -273,9 +265,9 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) - assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> + assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY && exit.taskId == task.taskId }).isTrue() @@ -294,9 +286,9 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) - assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> + assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY && exit.taskId == task.taskId }).isFalse() @@ -315,7 +307,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) assertThat(wct.hasBoundsChange(task.token)).isTrue() } @@ -333,13 +325,38 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) assertThat(wct.hasBoundsChange(task.token)).isFalse() } @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun exitImmersiveIfApplicable_byDisplay_withExcludeTask_doesNotExit() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + val transition = Binder() + desktopRepository.setTaskInFullImmersiveState( + displayId = DEFAULT_DISPLAY, + taskId = task.taskId, + immersive = true + ) + + controller.exitImmersiveIfApplicable( + wct = wct, + displayId = DEFAULT_DISPLAY, + excludeTaskId = task.taskId + )?.invoke(transition) + + assertThat(controller.pendingExternalExitTransitions.any { exit -> + exit.transition == transition && exit.displayId == DEFAULT_DISPLAY + && exit.taskId == task.taskId + }).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) fun exitImmersiveIfApplicable_byTask_inImmersive_changesTaskBounds() { val task = createFreeformTask() whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) @@ -350,7 +367,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task) + controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task) assertThat(wct.hasBoundsChange(task.token)).isTrue() } @@ -367,7 +384,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.exitImmersiveIfApplicable(wct, task) + controller.exitImmersiveIfApplicable(wct, task) assertThat(wct.hasBoundsChange(task.token)).isFalse() } @@ -385,9 +402,9 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(wct, task)?.invoke(transition) + controller.exitImmersiveIfApplicable(wct, task)?.invoke(transition) - assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> + assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY && exit.taskId == task.taskId }).isTrue() @@ -406,9 +423,9 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.exitImmersiveIfApplicable(wct, task)?.invoke(transition) + controller.exitImmersiveIfApplicable(wct, task)?.invoke(transition) - assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> + assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY && exit.taskId == task.taskId }).isFalse() @@ -416,7 +433,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) - fun onTransitionReady_pendingExit_removesPendingExit() { + fun onTransitionReady_pendingExit_removesPendingExitOnFinish() { val task = createFreeformTask() whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) val wct = WindowContainerTransaction() @@ -426,18 +443,19 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { taskId = task.taskId, immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) - immersiveHandler.onTransitionReady( + controller.onTransitionReady( transition = transition, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) + controller.onTransitionFinished(transition, aborted = false) - assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> + assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY && exit.taskId == task.taskId }).isFalse() @@ -445,6 +463,42 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun onTransitionReady_pendingExit_withMerge_removesPendingExitOnFinish() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + val transition = Binder() + val mergedToTransition = Binder() + desktopRepository.setTaskInFullImmersiveState( + displayId = DEFAULT_DISPLAY, + taskId = task.taskId, + immersive = true + ) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + + controller.onTransitionReady( + transition = transition, + info = createTransitionInfo( + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), + ) + controller.onTransitionMerged(transition, mergedToTransition) + controller.onTransitionFinished(mergedToTransition, aborted = false) + + assertThat(controller.pendingExternalExitTransitions.any { exit -> + exit.transition == transition && exit.displayId == DEFAULT_DISPLAY + && exit.taskId == task.taskId + }).isFalse() + assertThat(controller.pendingExternalExitTransitions.any { exit -> + exit.transition == mergedToTransition && exit.displayId == DEFAULT_DISPLAY + && exit.taskId == task.taskId + }).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) fun onTransitionReady_pendingExit_updatesRepository() { val task = createFreeformTask() whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) @@ -455,15 +509,15 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { taskId = task.taskId, immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) - immersiveHandler.onTransitionReady( + controller.onTransitionReady( transition = transition, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse() @@ -485,15 +539,15 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600)) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) - immersiveHandler.onTransitionReady( + controller.onTransitionReady( transition = transition, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.removeBoundsBeforeMaximize(task.taskId)).isNull() @@ -512,7 +566,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task) + controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task) assertThat( wct.hasBoundsChange(task.token, calculateMaximizeBounds(mockDisplayLayout, task)) @@ -536,7 +590,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { val preImmersiveBounds = Rect(100, 100, 500, 500) desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, preImmersiveBounds) - immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task) + controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task) assertThat( wct.hasBoundsChange(task.token, preImmersiveBounds) @@ -559,7 +613,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task) + controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task) assertThat( wct.hasBoundsChange(task.token, calculateInitialBounds(mockDisplayLayout, task)) @@ -577,13 +631,32 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { taskId = task.taskId, immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(wct, task)?.invoke(Binder()) + controller.exitImmersiveIfApplicable(wct, task)?.invoke(Binder()) - immersiveHandler.moveTaskToNonImmersive(task) + controller.moveTaskToNonImmersive(task) verify(mockTransitions, never()).startTransition(any(), any(), any()) } + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun exitImmersiveIfApplicable_inImmersive_isImmersiveChange() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + val transition = Binder() + val change = createChange(task) + desktopRepository.setTaskInFullImmersiveState( + displayId = DEFAULT_DISPLAY, + taskId = task.taskId, + immersive = true + ) + + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + + assertThat(controller.isImmersiveChange(transition, change)).isTrue() + } + private fun createTransitionInfo( @TransitionType type: Int = TRANSIT_CHANGE, @TransitionFlags flags: Int = 0, @@ -592,6 +665,11 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { changes.forEach { change -> addChange(change) } } + private fun createChange(task: RunningTaskInfo): TransitionInfo.Change = + TransitionInfo.Change(task.token, SurfaceControl()).apply { + taskInfo = task + } + private fun WindowContainerTransaction.hasBoundsChange(token: WindowContainerToken): Boolean = this.changes.any { change -> change.key == token.asBinder() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt index 81d59d586dd3..be0663cbd70d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt @@ -24,6 +24,9 @@ import android.app.WindowConfiguration.WindowingMode import android.os.Binder import android.os.Handler import android.os.IBinder +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.SurfaceControl @@ -33,6 +36,7 @@ import android.window.WindowContainerTransaction import androidx.test.filters.SmallTest import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE import com.android.internal.jank.InteractionJankMonitor +import com.android.window.flags.Flags import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.freeform.FreeformTaskTransitionHandler @@ -41,6 +45,7 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock @@ -60,6 +65,8 @@ import org.mockito.kotlin.whenever @RunWith(AndroidTestingRunner::class) class DesktopMixedTransitionHandlerTest : ShellTestCase() { + @JvmField @Rule val setFlagsRule = SetFlagsRule() + @Mock lateinit var transitions: Transitions @Mock lateinit var desktopRepository: DesktopRepository @Mock lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler @@ -106,6 +113,19 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { } @Test + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS) + fun startRemoveTransition_callsFreeformTaskTransitionHandler() { + val wct = WindowContainerTransaction() + whenever(freeformTaskTransitionHandler.startRemoveTransition(wct)) + .thenReturn(mock()) + + mixedHandler.startRemoveTransition(wct) + + verify(freeformTaskTransitionHandler).startRemoveTransition(wct) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS) fun startRemoveTransition_startsCloseTransition() { val wct = WindowContainerTransaction() whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler)) @@ -147,7 +167,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { fun startAnimation_withClosingDesktopTask_callsCloseTaskHandler() { val transition = mock<IBinder>() val transitionInfo = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)) - whenever(desktopRepository.getActiveNonMinimizedTaskCount(any())).thenReturn(2) + whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2) whenever( closeDesktopTaskTransitionHandler.startAnimation(any(), any(), any(), any(), any()) ) @@ -170,7 +190,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { fun startAnimation_withClosingLastDesktopTask_dispatchesTransition() { val transition = mock<IBinder>() val transitionInfo = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)) - whenever(desktopRepository.getActiveNonMinimizedTaskCount(any())).thenReturn(1) + whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(1) whenever(transitions.dispatchTransition(any(), any(), any(), any(), any(), any())) .thenReturn(mock()) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt index 3e2280393c2b..d90443c99d37 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt @@ -898,7 +898,7 @@ class DesktopRepositoryTest : ShellTestCase() { } @Test - fun getActiveNonMinimizedOrderedTasks_returnsFreeformTasksInCorrectOrder() { + fun getExpandedTasksOrdered_returnsFreeformTasksInCorrectOrder() { repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1) repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2) repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 3) @@ -907,13 +907,13 @@ class DesktopRepositoryTest : ShellTestCase() { repo.addOrMoveFreeformTaskToTop(displayId = 0, taskId = 2) repo.addOrMoveFreeformTaskToTop(displayId = 0, taskId = 1) - val tasks = repo.getActiveNonMinimizedOrderedTasks(displayId = 0) + val tasks = repo.getExpandedTasksOrdered(displayId = 0) assertThat(tasks).containsExactly(1, 2, 3).inOrder() } @Test - fun getActiveNonMinimizedOrderedTasks_excludesMinimizedTasks() { + fun getExpandedTasksOrdered_excludesMinimizedTasks() { repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1) repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2) repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 3) @@ -923,7 +923,7 @@ class DesktopRepositoryTest : ShellTestCase() { repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 1) repo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2) - val tasks = repo.getActiveNonMinimizedOrderedTasks(displayId = DEFAULT_DISPLAY) + val tasks = repo.getExpandedTasksOrdered(displayId = DEFAULT_DISPLAY) assertThat(tasks).containsExactly(1, 3).inOrder() } 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 7c336cdb54f6..bc2b36ccd835 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 @@ -201,7 +201,7 @@ class DesktopTasksControllerTest : ShellTestCase() { lateinit var toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler @Mock - lateinit var mockDesktopFullImmersiveTransitionHandler: DesktopFullImmersiveTransitionHandler + lateinit var mMockDesktopImmersiveController: DesktopImmersiveController @Mock lateinit var launchAdjacentController: LaunchAdjacentController @Mock lateinit var splitScreenController: SplitScreenController @Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler @@ -322,7 +322,7 @@ class DesktopTasksControllerTest : ShellTestCase() { dragAndDropTransitionHandler, toggleResizeDesktopTaskTransitionHandler, dragToDesktopTransitionHandler, - mockDesktopFullImmersiveTransitionHandler, + mMockDesktopImmersiveController, taskRepository, desktopModeLoggerTransitionObserver, launchAdjacentController, @@ -1773,7 +1773,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.minimizeTask(task) - verify(mockDesktopFullImmersiveTransitionHandler).exitImmersiveIfApplicable(any(), eq(task)) + verify(mMockDesktopImmersiveController).exitImmersiveIfApplicable(any(), eq(task)) } @Test @@ -1783,7 +1783,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val runOnTransit = RunOnStartTransitionCallback() whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any())) .thenReturn(transition) - whenever(mockDesktopFullImmersiveTransitionHandler.exitImmersiveIfApplicable(any(), eq(task))) + whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task))) .thenReturn(runOnTransit) controller.minimizeTask(task) @@ -3092,13 +3092,14 @@ class DesktopTasksControllerTest : ShellTestCase() { val transition = Binder() whenever(transitions.startTransition(eq(TRANSIT_OPEN), any(), anyOrNull())) .thenReturn(transition) - whenever(mockDesktopFullImmersiveTransitionHandler - .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId))).thenReturn(runOnStartTransit) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId))) + .thenReturn(runOnStartTransit) runOpenInstance(immersiveTask, freeformTask.taskId) - verify(mockDesktopFullImmersiveTransitionHandler) - .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId)) + verify(mMockDesktopImmersiveController) + .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId)) runOnStartTransit.assertOnlyInvocation(transition) } @@ -3445,7 +3446,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.toggleDesktopTaskFullImmersiveState(task) - verify(mockDesktopFullImmersiveTransitionHandler).moveTaskToImmersive(task) + verify(mMockDesktopImmersiveController).moveTaskToImmersive(task) } @Test @@ -3455,7 +3456,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.toggleDesktopTaskFullImmersiveState(task) - verify(mockDesktopFullImmersiveTransitionHandler).moveTaskToNonImmersive(task) + verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(task) } @Test @@ -3467,7 +3468,7 @@ class DesktopTasksControllerTest : ShellTestCase() { task.requestedVisibleTypes = WindowInsets.Type.statusBars() controller.onTaskInfoChanged(task) - verify(mockDesktopFullImmersiveTransitionHandler).moveTaskToNonImmersive(task) + verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(task) } @Test @@ -3479,7 +3480,7 @@ class DesktopTasksControllerTest : ShellTestCase() { task.requestedVisibleTypes = WindowInsets.Type.statusBars() controller.onTaskInfoChanged(task) - verify(mockDesktopFullImmersiveTransitionHandler, never()).moveTaskToNonImmersive(task) + verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(task) } @Test @@ -3488,13 +3489,14 @@ class DesktopTasksControllerTest : ShellTestCase() { val wct = WindowContainerTransaction() val runOnStartTransit = RunOnStartTransitionCallback() val transition = Binder() - whenever(mockDesktopFullImmersiveTransitionHandler - .exitImmersiveIfApplicable(wct, task.displayId)).thenReturn(runOnStartTransit) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)).thenReturn(runOnStartTransit) whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition) controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN) - verify(mockDesktopFullImmersiveTransitionHandler).exitImmersiveIfApplicable(wct, task.displayId) + verify(mMockDesktopImmersiveController) + .exitImmersiveIfApplicable(wct, task.displayId, task.taskId) runOnStartTransit.assertOnlyInvocation(transition) } @@ -3504,13 +3506,14 @@ class DesktopTasksControllerTest : ShellTestCase() { val wct = WindowContainerTransaction() val runOnStartTransit = RunOnStartTransitionCallback() val transition = Binder() - whenever(mockDesktopFullImmersiveTransitionHandler - .exitImmersiveIfApplicable(wct, task.displayId)).thenReturn(runOnStartTransit) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)).thenReturn(runOnStartTransit) whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition) controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN) - verify(mockDesktopFullImmersiveTransitionHandler).exitImmersiveIfApplicable(wct, task.displayId) + verify(mMockDesktopImmersiveController) + .exitImmersiveIfApplicable(wct, task.displayId, task.taskId) runOnStartTransit.assertOnlyInvocation(transition) } @@ -3519,14 +3522,15 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(background = true) val runOnStartTransit = RunOnStartTransitionCallback() val transition = Binder() - whenever(mockDesktopFullImmersiveTransitionHandler - .exitImmersiveIfApplicable(any(), eq(task.displayId))).thenReturn(runOnStartTransit) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId))) + .thenReturn(runOnStartTransit) whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition) controller.moveTaskToFront(task.taskId, remoteTransition = null) - verify(mockDesktopFullImmersiveTransitionHandler) - .exitImmersiveIfApplicable(any(), eq(task.displayId)) + verify(mMockDesktopImmersiveController) + .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)) runOnStartTransit.assertOnlyInvocation(transition) } @@ -3535,14 +3539,15 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(background = false) val runOnStartTransit = RunOnStartTransitionCallback() val transition = Binder() - whenever(mockDesktopFullImmersiveTransitionHandler - .exitImmersiveIfApplicable(any(), eq(task.displayId))).thenReturn(runOnStartTransit) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId))) + .thenReturn(runOnStartTransit) whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition) controller.moveTaskToFront(task.taskId, remoteTransition = null) - verify(mockDesktopFullImmersiveTransitionHandler) - .exitImmersiveIfApplicable(any(), eq(task.displayId)) + verify(mMockDesktopImmersiveController) + .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)) runOnStartTransit.assertOnlyInvocation(transition) } @@ -3555,7 +3560,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.handleRequest(binder, createTransition(task)) - verify(mockDesktopFullImmersiveTransitionHandler) + verify(mMockDesktopImmersiveController) .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId)) } @@ -3567,10 +3572,117 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.handleRequest(binder, createTransition(task)) - verify(mockDesktopFullImmersiveTransitionHandler) + verify(mMockDesktopImmersiveController) .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId)) } + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_notShowingDesktop_doesNotPlay() { + val triggerTask = setUpFullscreenTask(displayId = 5) + taskRepository.setTaskInFullImmersiveState( + displayId = triggerTask.displayId, + taskId = triggerTask.taskId, + immersive = true + ) + + assertThat(controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + )).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_notOpening_doesNotPlay() { + val triggerTask = setUpFreeformTask(displayId = 5) + taskRepository.setTaskInFullImmersiveState( + displayId = triggerTask.displayId, + taskId = triggerTask.taskId, + immersive = true + ) + + assertThat(controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_CHANGE, triggerTask, /* remoteTransition= */ null) + )).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_notImmersive_doesNotPlay() { + val triggerTask = setUpFreeformTask(displayId = 5) + taskRepository.setTaskInFullImmersiveState( + displayId = triggerTask.displayId, + taskId = triggerTask.taskId, + immersive = false + ) + + assertThat(controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + )).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_fullscreenEntersDesktop_plays() { + // At least one freeform task to be in a desktop. + val existingTask = setUpFreeformTask(displayId = 5) + val triggerTask = setUpFullscreenTask(displayId = 5) + assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue() + taskRepository.setTaskInFullImmersiveState( + displayId = existingTask.displayId, + taskId = existingTask.taskId, + immersive = true + ) + + assertThat( + controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + ) + ).isTrue() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_fullscreenStaysFullscreen_doesNotPlay() { + val triggerTask = setUpFullscreenTask(displayId = 5) + assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse() + + assertThat(controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + )).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_freeformStaysInDesktop_plays() { + // At least one freeform task to be in a desktop. + val existingTask = setUpFreeformTask(displayId = 5) + val triggerTask = setUpFreeformTask(displayId = 5, active = false) + assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue() + taskRepository.setTaskInFullImmersiveState( + displayId = existingTask.displayId, + taskId = existingTask.taskId, + immersive = true + ) + + assertThat( + controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + ) + ).isTrue() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_freeformExitsDesktop_doesNotPlay() { + val triggerTask = setUpFreeformTask(displayId = 5, active = false) + assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse() + + assertThat(controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + )).isFalse() + } + private class RunOnStartTransitionCallback : ((IBinder) -> Unit) { var invocations = 0 private set 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 index 596b76dbdb2e..4d7e47fa51bd 100644 --- 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 @@ -303,12 +303,12 @@ class DesktopTasksLimiterTest : ShellTestCase() { } @Test - fun addAndGetMinimizeTaskChangesIfNeeded_tasksWithinLimit_noTaskMinimized() { + fun addAndGetMinimizeTaskChanges_tasksWithinLimit_noTaskMinimized() { (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() } val wct = WindowContainerTransaction() val minimizedTaskId = - desktopTasksLimiter.addAndGetMinimizeTaskChangesIfNeeded( + desktopTasksLimiter.addAndGetMinimizeTaskChanges( displayId = DEFAULT_DISPLAY, wct = wct, newFrontTaskId = setUpFreeformTask().taskId) @@ -318,31 +318,31 @@ class DesktopTasksLimiterTest : ShellTestCase() { } @Test - fun addAndGetMinimizeTaskChangesIfNeeded_tasksAboveLimit_backTaskMinimized() { + fun addAndGetMinimizeTaskChanges_tasksAboveLimit_backTaskMinimized() { // The following list will be ordered bottom -> top, as the last task is moved to top last. val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() } val wct = WindowContainerTransaction() val minimizedTaskId = - desktopTasksLimiter.addAndGetMinimizeTaskChangesIfNeeded( + desktopTasksLimiter.addAndGetMinimizeTaskChanges( displayId = DEFAULT_DISPLAY, wct = wct, newFrontTaskId = setUpFreeformTask().taskId) - assertThat(minimizedTaskId).isEqualTo(tasks.first()) + assertThat(minimizedTaskId).isEqualTo(tasks.first().taskId) 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() { + fun addAndGetMinimizeTaskChanges_nonMinimizedTasksWithinLimit_noTaskMinimized() { val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() } desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = tasks[0].taskId) val wct = WindowContainerTransaction() val minimizedTaskId = - desktopTasksLimiter.addAndGetMinimizeTaskChangesIfNeeded( + desktopTasksLimiter.addAndGetMinimizeTaskChanges( displayId = 0, wct = wct, newFrontTaskId = setUpFreeformTask().taskId) @@ -352,50 +352,50 @@ class DesktopTasksLimiterTest : ShellTestCase() { } @Test - fun getTaskToMinimizeIfNeeded_tasksWithinLimit_returnsNull() { + fun getTaskToMinimize_tasksWithinLimit_returnsNull() { val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() } - val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded( - visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId }) + val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize( + visibleOrderedTasks = tasks.map { it.taskId }) assertThat(minimizedTask).isNull() } @Test - fun getTaskToMinimizeIfNeeded_tasksAboveLimit_returnsBackTask() { + fun getTaskToMinimize_tasksAboveLimit_returnsBackTask() { val tasks = (1..MAX_TASK_LIMIT + 1).map { setUpFreeformTask() } - val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded( - visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId }) + val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize( + visibleOrderedTasks = tasks.map { it.taskId }) // first == front, last == back - assertThat(minimizedTask).isEqualTo(tasks.last()) + assertThat(minimizedTask).isEqualTo(tasks.last().taskId) } @Test - fun getTaskToMinimizeIfNeeded_tasksAboveLimit_otherLimit_returnsBackTask() { + fun getTaskToMinimize_tasksAboveLimit_otherLimit_returnsBackTask() { desktopTasksLimiter = DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT2, interactionJankMonitor, mContext, handler) val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() } - val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded( - visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId }) + val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize( + visibleOrderedTasks = tasks.map { it.taskId }) // first == front, last == back - assertThat(minimizedTask).isEqualTo(tasks.last()) + assertThat(minimizedTask).isEqualTo(tasks.last().taskId) } @Test - fun getTaskToMinimizeIfNeeded_withNewTask_tasksAboveLimit_returnsBackTask() { + fun getTaskToMinimize_withNewTask_tasksAboveLimit_returnsBackTask() { val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() } - val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded( - visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId }, + val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize( + visibleOrderedTasks = tasks.map { it.taskId }, newTaskIdInFront = setUpFreeformTask().taskId) // first == front, last == back - assertThat(minimizedTask).isEqualTo(tasks.last()) + assertThat(minimizedTask).isEqualTo(tasks.last().taskId) } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt index 1e105d9588ab..7dbadc9d9083 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.desktopmode.education +import android.os.SystemProperties import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule @@ -50,6 +51,7 @@ import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Mock import org.mockito.MockitoAnnotations import org.mockito.kotlin.any @@ -70,7 +72,10 @@ class AppHandleEducationControllerTest : ShellTestCase() { @JvmField @Rule val extendedMockitoRule = - ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!! + ExtendedMockitoRule.Builder(this) + .mockStatic(DesktopModeStatus::class.java) + .mockStatic(SystemProperties::class.java) + .build()!! @JvmField @Rule val setFlagsRule = SetFlagsRule() private lateinit var educationController: AppHandleEducationController @@ -189,6 +194,29 @@ class AppHandleEducationControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) + fun overridePrerequisite_educationViewedAlready_shouldCallShowEducationTooltip() = + testScope.runTest { + // App handle is visible but education has been viewed before. But as we are overriding + // prerequisite conditions, we should show education tooltip. + // Mark education viewed. + testDataStoreFlow.value = + createWindowingEducationProto(educationViewedTimestampMillis = 123L) + val systemPropertiesKey = + "persist.desktop_windowing_app_handle_education_override_conditions" + whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean())) + .thenReturn(true) + setShouldShowAppHandleEducation(true) + + // Simulate app handle visible. + testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false) + // Wait for first tooltip to showup. + waitForBufferDelay() + + verify(mockTooltipController, times(1)).showEducationTooltip(any(), any()) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) fun init_appHandleExpanded_shouldMarkFeatureViewed() = testScope.runTest { setShouldShowAppHandleEducation(false) @@ -454,7 +482,7 @@ class AppHandleEducationControllerTest : ShellTestCase() { .thenReturn(shouldShowAppHandleEducation) /** - * Class under test waits for some seconds before showing education, simulate advance time before + * Class under test waits for some time before showing education, simulate advance time before * verifying or moving forward */ private fun TestScope.waitForBufferDelay() { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt index ac994248c962..a3e74e8aed5d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt @@ -19,10 +19,12 @@ package com.android.wm.shell.desktopmode.education import android.app.usage.UsageStats import android.app.usage.UsageStatsManager import android.content.Context +import android.os.SystemProperties import android.testing.AndroidTestingRunner import android.testing.TestableContext import android.testing.TestableResources import androidx.test.filters.SmallTest +import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.wm.shell.R import com.android.wm.shell.ShellTestCase import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository @@ -35,18 +37,26 @@ import com.google.common.truth.Truth.assertThat import kotlin.Int.Companion.MAX_VALUE import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever -/** Tests of [AppHandleEducationFilter] - * Usage: atest AppHandleEducationFilterTest */ +/** Tests of [AppHandleEducationFilter] Usage: atest AppHandleEducationFilterTest */ @SmallTest @RunWith(AndroidTestingRunner::class) +@kotlinx.coroutines.ExperimentalCoroutinesApi class AppHandleEducationFilterTest : ShellTestCase() { + @JvmField + @Rule + val extendedMockitoRule = + ExtendedMockitoRule.Builder(this).mockStatic(SystemProperties::class.java).build()!! @Mock private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository @Mock private lateinit var mockUsageStatsManager: UsageStatsManager private lateinit var educationFilter: AppHandleEducationFilter @@ -209,4 +219,23 @@ class AppHandleEducationFilterTest : ShellTestCase() { // We should not show app handle education if app menu is expanded assertThat(result).isFalse() } + + @Test + fun shouldShowAppHandleEducation_overridePrerequisite_returnsTrue() = runTest { + // Simulate that gmail app has been launched twice before, minimum app launch count is 3, hence + // #shouldShowAppHandleEducation should return false. But as we are overriding prerequisite + // conditions, #shouldShowAppHandleEducation should return true. + testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3) + val systemPropertiesKey = "persist.desktop_windowing_app_handle_education_override_conditions" + whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean())).thenReturn(true) + val windowingEducationProto = + createWindowingEducationProto( + appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2), + appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) + `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) + + val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState()) + + assertThat(result).isTrue() + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java index 7ae0bcd13681..90ab2b8285cd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java @@ -43,7 +43,7 @@ import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; import com.android.window.flags.Flags; -import com.android.wm.shell.desktopmode.DesktopFullImmersiveTransitionHandler; +import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.TransitionInfoBuilder; @@ -70,7 +70,7 @@ public class FreeformTaskTransitionObserverTest { @Mock private Transitions mTransitions; @Mock - private DesktopFullImmersiveTransitionHandler mDesktopFullImmersiveTransitionHandler; + private DesktopImmersiveController mDesktopImmersiveController; @Mock private WindowDecorViewModel mWindowDecorViewModel; @Mock @@ -92,7 +92,7 @@ public class FreeformTaskTransitionObserverTest { mTransitionObserver = new FreeformTaskTransitionObserver( context, mShellInit, mTransitions, - Optional.of(mDesktopFullImmersiveTransitionHandler), + Optional.of(mDesktopImmersiveController), mWindowDecorViewModel, Optional.of(mTaskChangeListener), mFocusTransitionObserver); final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass( @@ -321,7 +321,7 @@ public class FreeformTaskTransitionObserverTest { @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) - public void onTransitionReady_forwardsToDesktopImmersiveHandler() { + public void onTransitionReady_forwardsToDesktopImmersiveController() { final IBinder transition = mock(IBinder.class); final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CHANGE, 0).build(); final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); @@ -329,7 +329,38 @@ public class FreeformTaskTransitionObserverTest { mTransitionObserver.onTransitionReady(transition, info, startT, finishT); - verify(mDesktopFullImmersiveTransitionHandler).onTransitionReady(transition, info); + verify(mDesktopImmersiveController).onTransitionReady(transition, info, startT, finishT); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + public void onTransitionMerged_forwardsToDesktopImmersiveController() { + final IBinder merged = mock(IBinder.class); + final IBinder playing = mock(IBinder.class); + + mTransitionObserver.onTransitionMerged(merged, playing); + + verify(mDesktopImmersiveController).onTransitionMerged(merged, playing); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + public void onTransitionStarting_forwardsToDesktopImmersiveController() { + final IBinder transition = mock(IBinder.class); + + mTransitionObserver.onTransitionStarting(transition); + + verify(mDesktopImmersiveController).onTransitionStarting(transition); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + public void onTransitionFinished_forwardsToDesktopImmersiveController() { + final IBinder transition = mock(IBinder.class); + + mTransitionObserver.onTransitionFinished(transition, /* aborted= */ false); + + verify(mDesktopImmersiveController).onTransitionFinished(transition, /* aborted= */ false); } private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java index 66f8c0b9558d..880ca2ce0cf9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java @@ -124,7 +124,7 @@ public class PipResizeGestureHandlerTest extends ShellTestCase { mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm, mPipBoundsState, motionHelper, mPipTouchState, mPipTaskOrganizer, mPipDismissTargetHandler, - () -> {}, mPipUiEventLogger, mPhonePipMenuController, + (Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController, mMainExecutor, null /* pipPerfHintController */) { @Override public void pilferPointers() { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java index 0effc3e3d6b8..6087763b4978 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java @@ -20,6 +20,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -43,6 +44,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.internal.os.IResultReceiver; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; @@ -141,8 +143,9 @@ public class RecentsTransitionHandlerTest extends ShellTestCase { } @Test - public void testStartSyntheticRecentsTransition_callsOnAnimationStart() throws Exception { + public void testStartSyntheticRecentsTransition_callsOnAnimationStartAndFinishCallback() throws Exception { final IRecentsAnimationRunner runner = mock(IRecentsAnimationRunner.class); + final IResultReceiver finishCallback = mock(IResultReceiver.class); doReturn(new Binder()).when(runner).asBinder(); Bundle options = new Bundle(); options.putBoolean("is_synthetic_recents_transition", true); @@ -151,10 +154,11 @@ public class RecentsTransitionHandlerTest extends ShellTestCase { runner); verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any()); - // Finish and verify no transition remains + // Finish and verify no transition remains and that the provided finish callback is called mRecentsTransitionHandler.findController(transition).finish(true /* toHome */, - false /* sendUserLeaveHint */, null /* finishCb */); + false /* sendUserLeaveHint */, finishCallback); mMainExecutor.flushAll(); + verify(finishCallback).send(anyInt(), any()); assertNull(mRecentsTransitionHandler.findController(transition)); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt index 24f6becc3536..a20a89c644ed 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt @@ -36,6 +36,7 @@ import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM +import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP import com.google.common.truth.Truth.assertThat @@ -48,9 +49,9 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.any -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations import org.mockito.quality.Strictness +import org.mockito.Mockito.`when` as whenever /** * Tests for [DragPositioningCallbackUtility]. @@ -193,6 +194,62 @@ class DragPositioningCallbackUtilityTest { @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) + fun testChangeBounds_unresizeableApp_initialHeightLessThanMin_increasingBounds_resizeAllowed() { + mockWindowDecoration.mTaskInfo.isResizeable = false + val startingPoint = PointF(BELOW_MIN_HEIGHT_BOUNDS.right.toFloat(), + BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(BELOW_MIN_HEIGHT_BOUNDS) + + // Resize to increased bounds + val newX = BELOW_MIN_HEIGHT_BOUNDS.right.toFloat() + 20 + val newY = BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat() + 10 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is in direction of desired range + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, BELOW_MIN_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.right + 20) + assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.bottom + 10) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) + fun testChangeBounds_unresizeableApp_initialHeightMoreThanMax_decreasingBounds_resizeAllowed() { + mockWindowDecoration.mTaskInfo.isResizeable = false + val startingPoint = PointF(EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat(), + EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect(EXCEEDS_MAX_HEIGHT_BOUNDS) + + // Resize to decreased bounds. + val newX = EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat() - 10 + val newY = EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat() + 20 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is in direction of desired range. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, + repositionTaskBounds, EXCEEDS_MAX_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.top + 20) + assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.right - 10) + assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.bottom) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_widthLessThanMin_resetToStartingBounds() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) @@ -211,13 +268,68 @@ class DragPositioningCallbackUtilityTest { ) ) - assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right) assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) } + @Test + @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) + fun testChangeBounds_unresizeableApp_initialWidthLessThanMin_increasingBounds_resizeAllowed() { + mockWindowDecoration.mTaskInfo.isResizeable = false + val startingPoint = PointF(BELOW_MIN_WIDTH_BOUNDS.right.toFloat(), + BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(BELOW_MIN_WIDTH_BOUNDS) + + // Resize to increased bounds. + val newX = BELOW_MIN_WIDTH_BOUNDS.right.toFloat() + 10 + val newY = BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat() + 20 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is in direction of desired range. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, BELOW_MIN_WIDTH_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.right + 10) + assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.bottom + 20) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) + fun testChangeBounds_unresizeableApp_initialWidthMoreThanMax_decreasingBounds_resizeAllowed() { + mockWindowDecoration.mTaskInfo.isResizeable = false + val startingPoint = PointF(EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat(), + EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect(EXCEEDS_MAX_WIDTH_BOUNDS) + + // Resize to decreased bounds. + val newX = EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat() + 20 + val newY = EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat() + 10 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is in direction of desired range. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_LEFT or CTRL_TYPE_TOP, + repositionTaskBounds, EXCEEDS_MAX_WIDTH_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.left + 20) + assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.top + 10) + assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.right) + assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.bottom) + } + @Test fun testChangeBoundsDoesNotChangeHeightWhenNegative() { @@ -427,6 +539,60 @@ class DragPositioningCallbackUtilityTest { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) + fun testMinHeight_initialHeightLessThanMin_increasingHeight_resizeAllowed() { + val startingPoint = PointF(BELOW_MIN_HEIGHT_BOUNDS.right.toFloat(), + BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(BELOW_MIN_HEIGHT_BOUNDS) + + // Attempt to increase height. + val newX = BELOW_MIN_HEIGHT_BOUNDS.right.toFloat() + val newY = BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat() + 10 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is increasing height closer to valid region. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, BELOW_MIN_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.right) + assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.bottom + 10) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) + fun testMinWidth_initialWidthLessThanMin_increasingBounds_resizeAllowed() { + val startingPoint = PointF(BELOW_MIN_WIDTH_BOUNDS.right.toFloat(), + BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(BELOW_MIN_WIDTH_BOUNDS) + + // Attempt to increase width. + val newX = BELOW_MIN_WIDTH_BOUNDS.right.toFloat() + 10 + val newY = BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat() + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is increasing width closer to valid region. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, BELOW_MIN_WIDTH_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.right + 10) + assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.bottom) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() { doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(mockContext) } initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1) @@ -547,6 +713,61 @@ class DragPositioningCallbackUtilityTest { assertThat(repositionTaskBounds.height()).isLessThan(STABLE_BOUNDS.bottom) } + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) + fun testMaxHeight_initialHeightMoreThanMax_decreasingHeight_resizeAllowed() { + mockWindowDecoration.mTaskInfo.isResizeable = false + val startingPoint = PointF(EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat(), + EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect(EXCEEDS_MAX_HEIGHT_BOUNDS) + + // Attempt to decrease height + val newX = EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat() - 10 + val newY = EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat() + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is decreasing height closer to valid region. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, + repositionTaskBounds, EXCEEDS_MAX_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.right - 10) + assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.bottom ) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) + fun testMaxHeight_initialWidthMoreThanMax_decreasingBounds_resizeAllowed() { + val startingPoint = PointF(EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat(), + EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect(EXCEEDS_MAX_WIDTH_BOUNDS) + + // Attempt to decrease width. + val newX = EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat() + 20 + val newY = EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat() + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is decreasing width closer to valid region. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_LEFT or CTRL_TYPE_TOP, + repositionTaskBounds, EXCEEDS_MAX_WIDTH_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.left + 20) + assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.right) + assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.bottom) + } + private fun initializeTaskInfo(taskMinWidth: Int = MIN_WIDTH, taskMinHeight: Int = MIN_HEIGHT) { mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { taskId = TASK_ID @@ -571,6 +792,10 @@ class DragPositioningCallbackUtilityTest { private const val NAVBAR_HEIGHT = 50 private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600) private val STARTING_BOUNDS = Rect(0, 0, 100, 100) + private val BELOW_MIN_WIDTH_BOUNDS = Rect(0, 0, 50, 100) + private val BELOW_MIN_HEIGHT_BOUNDS = Rect(0, 0, 100, 50) + private val EXCEEDS_MAX_WIDTH_BOUNDS = Rect(0, 0, 3000, 1500) + private val EXCEEDS_MAX_HEIGHT_BOUNDS = Rect(0, 0, 1000, 2000) private val OFF_CENTER_STARTING_BOUNDS = Rect(-100, -100, 10, 10) private val DISALLOWED_RESIZE_AREA = Rect( DISPLAY_BOUNDS.left, diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index a39f30bbad1f..1bc15d72bacc 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -262,6 +262,8 @@ cc_test { "tests/data/**/*.apk", "tests/data/**/*.arsc", "tests/data/**/*.idmap", + ], + device_common_data: [ ":FrameworkResourcesSparseTestApp", ":FrameworkResourcesNotSparseTestApp", ], diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp index 28856c87f7c6..8af4b7e8f4c8 100644 --- a/libs/protoutil/Android.bp +++ b/libs/protoutil/Android.bp @@ -60,6 +60,7 @@ cc_library { "//apex_available:platform", "com.android.os.statsd", "test_com.android.os.statsd", + "com.android.uprobestats", ], } |