diff options
Diffstat (limited to 'libs')
566 files changed, 12219 insertions, 5623 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 9da6c10c6d74..a663f9fafb50 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -16,11 +16,13 @@ package androidx.window.extensions; +import android.app.ActivityTaskManager; import android.app.ActivityThread; import android.app.Application; import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.RawFoldingFeatureProducer; import androidx.window.extensions.area.WindowAreaComponent; @@ -104,9 +106,13 @@ public class WindowExtensionsImpl implements WindowExtensions { * {@link WindowExtensions#getWindowLayoutComponent()}. * @return {@link ActivityEmbeddingComponent} OEM implementation. */ - @NonNull + @Nullable public ActivityEmbeddingComponent getActivityEmbeddingComponent() { if (mSplitController == null) { + if (!ActivityTaskManager.supportsMultiWindow(getApplication())) { + // Disable AE for device that doesn't support multi window. + return null; + } synchronized (mLock) { if (mSplitController == null) { mSplitController = new SplitController( diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 381e9d472f0f..08b7bb89d10c 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -25,6 +25,7 @@ import android.window.TaskFragmentParentInfo; import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.window.extensions.core.util.function.Function; /** @@ -186,10 +187,21 @@ class SplitContainer { return (mSplitRule instanceof SplitPlaceholderRule); } - @NonNull - SplitInfo toSplitInfo() { - return new SplitInfo(mPrimaryContainer.toActivityStack(), - mSecondaryContainer.toActivityStack(), mCurrentSplitAttributes, mToken); + /** + * Returns the SplitInfo representing this container. + * + * @return the SplitInfo representing this container if the underlying TaskFragmentContainers + * are stable, or {@code null} if any TaskFragmentContainer is in an intermediate state. + */ + @Nullable + SplitInfo toSplitInfoIfStable() { + final ActivityStack primaryActivityStack = mPrimaryContainer.toActivityStackIfStable(); + final ActivityStack secondaryActivityStack = mSecondaryContainer.toActivityStackIfStable(); + if (primaryActivityStack == null || secondaryActivityStack == null) { + return null; + } + return new SplitInfo(primaryActivityStack, secondaryActivityStack, + mCurrentSplitAttributes, mToken); } static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) { 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 3ad30457697e..cdfc4c87d271 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -879,14 +879,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Skip resolving if the activity is on a pinned TaskFragmentContainer. // TODO(b/243518738): skip resolving for overlay container. - if (container != null) { - final TaskContainer taskContainer = container.getTaskContainer(); - if (taskContainer.isTaskFragmentContainerPinned(container)) { - return true; - } + final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null; + if (container != null && taskContainer != null + && taskContainer.isTaskFragmentContainerPinned(container)) { + return true; } - final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null; if (!isOnReparent && taskContainer != null && taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */) != container) { @@ -895,6 +893,28 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return true; } + // Ensure the top TaskFragments are updated to the right config if activity is resolved + // to a new TaskFragment while pin TF exists. + final boolean handled = resolveActivityToContainerByRule(wct, activity, container, + isOnReparent); + if (handled && taskContainer != null) { + final SplitPinContainer splitPinContainer = taskContainer.getSplitPinContainer(); + if (splitPinContainer != null) { + final TaskFragmentContainer resolvedContainer = getContainerWithActivity(activity); + if (resolvedContainer != null && resolvedContainer.getRunningActivityCount() <= 1) { + updateContainer(wct, splitPinContainer.getSecondaryContainer()); + } + } + } + return handled; + } + + /** + * Resolves the activity to a {@link TaskFragmentContainer} according to the Split-rules. + */ + boolean resolveActivityToContainerByRule(@NonNull WindowContainerTransaction wct, + @NonNull Activity activity, @Nullable TaskFragmentContainer container, + boolean isOnReparent) { /* * We will check the following to see if there is any embedding rule matched: * 1. Whether the new launched activity should always expand. @@ -1301,6 +1321,26 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } + // Ensure the top TaskFragments are updated to the right config if the intent is resolved + // to a new TaskFragment while pin TF exists. + final TaskFragmentContainer launchingContainer = resolveStartActivityIntentByRule(wct, + taskId, intent, launchingActivity); + if (launchingContainer != null && launchingContainer.getRunningActivityCount() == 0) { + final SplitPinContainer splitPinContainer = + launchingContainer.getTaskContainer().getSplitPinContainer(); + if (splitPinContainer != null) { + updateContainer(wct, splitPinContainer.getSecondaryContainer()); + } + } + return launchingContainer; + } + + /** + * Resolves the intent to a {@link TaskFragmentContainer} according to the Split-rules. + */ + @Nullable + TaskFragmentContainer resolveStartActivityIntentByRule(@NonNull WindowContainerTransaction wct, + int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) { /* * We will check the following to see if there is any embedding rule matched: * 1. Whether the new activity intent should always expand. @@ -1972,8 +2012,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (mEmbeddingCallback == null || !readyToReportToClient()) { return; } - final List<SplitInfo> currentSplitStates = getActiveSplitStates(); - if (mLastReportedSplitStates.equals(currentSplitStates)) { + final List<SplitInfo> currentSplitStates = getActiveSplitStatesIfStable(); + if (currentSplitStates == null || mLastReportedSplitStates.equals(currentSplitStates)) { return; } mLastReportedSplitStates.clear(); @@ -1983,13 +2023,21 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen /** * Returns a list of descriptors for currently active split states. + * + * @return a list of descriptors for currently active split states if all the containers are in + * a stable state, or {@code null} otherwise. */ @GuardedBy("mLock") - @NonNull - private List<SplitInfo> getActiveSplitStates() { + @Nullable + private List<SplitInfo> getActiveSplitStatesIfStable() { final List<SplitInfo> splitStates = new ArrayList<>(); for (int i = mTaskContainers.size() - 1; i >= 0; i--) { - mTaskContainers.valueAt(i).getSplitStates(splitStates); + final List<SplitInfo> taskSplitStates = + mTaskContainers.valueAt(i).getSplitStatesIfStable(); + if (taskSplitStates == null) { + return null; + } + splitStates.addAll(taskSplitStates); } return splitStates; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 463c8ceaf992..9a0769a82d99 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -359,11 +359,23 @@ class TaskContainer { } } - /** Adds the descriptors of split states in this Task to {@code outSplitStates}. */ - void getSplitStates(@NonNull List<SplitInfo> outSplitStates) { + /** + * Gets the descriptors of split states in this Task. + * + * @return a list of {@code SplitInfo} if all the SplitContainers are stable, or {@code null} if + * any SplitContainer is in an intermediate state. + */ + @Nullable + List<SplitInfo> getSplitStatesIfStable() { + final List<SplitInfo> splitStates = new ArrayList<>(); for (SplitContainer container : mSplitContainers) { - outSplitStates.add(container.toSplitInfo()); + final SplitInfo splitInfo = container.toSplitInfoIfStable(); + if (splitInfo == null) { + return null; + } + splitStates.add(splitInfo); } + return splitStates; } /** A wrapper class which contains the information of {@link TaskContainer} */ diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 61df335515b8..0a694b5c3b64 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -217,6 +217,30 @@ class TaskFragmentContainer { /** List of non-finishing activities that belong to this container and live in this process. */ @NonNull List<Activity> collectNonFinishingActivities() { + final List<Activity> activities = collectNonFinishingActivities(false /* checkIfStable */); + if (activities == null) { + throw new IllegalStateException( + "Result activities should never be null when checkIfstable is false."); + } + return activities; + } + + /** + * Collects non-finishing activities that belong to this container and live in this process. + * + * @param checkIfStable if {@code true}, returns {@code null} when the container is in an + * intermediate state. + * @return List of non-finishing activities that belong to this container and live in this + * process, {@code null} if checkIfStable is {@code true} and the container is in an + * intermediate state. + */ + @Nullable + List<Activity> collectNonFinishingActivities(boolean checkIfStable) { + if (checkIfStable + && (mInfo == null || mInfo.isEmpty() || !mPendingAppearedActivities.isEmpty())) { + return null; + } + final List<Activity> allActivities = new ArrayList<>(); if (mInfo != null) { // Add activities reported from the server. @@ -224,6 +248,15 @@ class TaskFragmentContainer { final Activity activity = mController.getActivity(token); if (activity != null && !activity.isFinishing()) { allActivities.add(activity); + } else { + if (checkIfStable) { + // Return null except for a special case when the activity is started in + // background. + if (activity == null && !mTaskContainer.isVisible()) { + continue; + } + return null; + } } } } @@ -277,9 +310,19 @@ class TaskFragmentContainer { return false; } - @NonNull - ActivityStack toActivityStack() { - return new ActivityStack(collectNonFinishingActivities(), isEmpty(), mToken); + /** + * Returns the ActivityStack representing this container. + * + * @return ActivityStack representing this container if it is in a stable state. {@code null} if + * in an intermediate state. + */ + @Nullable + ActivityStack toActivityStackIfStable() { + final List<Activity> activities = collectNonFinishingActivities(true /* checkIfStable */); + if (activities == null) { + return null; + } + return new ActivityStack(activities, isEmpty(), mToken); } /** Adds the activity that will be reparented to this container. */ 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 a1fe7f75a826..9b84a48cdbda 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -17,6 +17,7 @@ package androidx.window.extensions.layout; import static android.view.Display.DEFAULT_DISPLAY; + import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT; import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED; import static androidx.window.util.ExtensionHelper.isZero; @@ -94,14 +95,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); } - /** Registers to listen to {@link CommonFoldingFeature} changes */ - public void addFoldingStateChangedCallback( - java.util.function.Consumer<List<CommonFoldingFeature>> consumer) { - synchronized (mLock) { - mFoldingFeatureProducer.addDataChangedCallback(consumer); - } - } - /** * Adds a listener interested in receiving updates to {@link WindowLayoutInfo} * diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java index a0590dc2c832..9607b78bacf0 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java @@ -16,8 +16,10 @@ package androidx.window.extensions; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.google.common.truth.Truth.assertThat; +import android.app.ActivityTaskManager; import android.platform.test.annotations.Presubmit; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -52,7 +54,11 @@ public class WindowExtensionsTest { @Test public void testGetActivityEmbeddingComponent() { - assertThat(mExtensions.getActivityEmbeddingComponent()).isNotNull(); + if (ActivityTaskManager.supportsMultiWindow(getInstrumentation().getContext())) { + assertThat(mExtensions.getActivityEmbeddingComponent()).isNotNull(); + } else { + assertThat(mExtensions.getActivityEmbeddingComponent()).isNull(); + } } @Test 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 b2ffad7a74e4..d440a3eb95de 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 @@ -1286,6 +1286,34 @@ public class SplitControllerTest { } @Test + public void testSplitInfoCallback_NotReportSplitIfUnstable() { + final Activity r0 = createMockActivity(); + final Activity r1 = createMockActivity(); + addSplitTaskFragments(r0, r1); + + // Should report new SplitInfo list if stable. + mSplitController.updateCallbackIfNecessary(); + assertEquals(1, mSplitInfos.size()); + + // Should not report new SplitInfo list if unstable, e.g. any Activity is finishing. + mSplitInfos.clear(); + final Activity r2 = createMockActivity(); + final Activity r3 = createMockActivity(); + doReturn(true).when(r2).isFinishing(); + addSplitTaskFragments(r2, r3); + + mSplitController.updateCallbackIfNecessary(); + assertTrue(mSplitInfos.isEmpty()); + + // Should report SplitInfo list if it becomes stable again. + mSplitInfos.clear(); + doReturn(false).when(r2).isFinishing(); + + mSplitController.updateCallbackIfNecessary(); + assertEquals(2, mSplitInfos.size()); + } + + @Test public void testSplitInfoCallback_reportSplitInMultipleTasks() { final int taskId0 = 1; final int taskId1 = 2; diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index 000c65a75c81..21889960a8b2 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -48,6 +48,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.List; + /** * Test class for {@link TaskContainer}. * @@ -165,4 +167,35 @@ public class TaskContainerTest { doReturn(activity1).when(tf1).getTopNonFinishingActivity(); assertEquals(activity1, taskContainer.getTopNonFinishingActivity()); } + + @Test + public void testGetSplitStatesIfStable() { + final TaskContainer taskContainer = createTestTaskContainer(); + + final SplitContainer splitContainer0 = mock(SplitContainer.class); + final SplitContainer splitContainer1 = mock(SplitContainer.class); + final SplitInfo splitInfo0 = mock(SplitInfo.class); + final SplitInfo splitInfo1 = mock(SplitInfo.class); + taskContainer.addSplitContainer(splitContainer0); + taskContainer.addSplitContainer(splitContainer1); + + // When all the SplitContainers are stable, getSplitStatesIfStable() returns the list of + // SplitInfo representing the SplitContainers. + doReturn(splitInfo0).when(splitContainer0).toSplitInfoIfStable(); + doReturn(splitInfo1).when(splitContainer1).toSplitInfoIfStable(); + + List<SplitInfo> splitInfoList = taskContainer.getSplitStatesIfStable(); + + assertEquals(2, splitInfoList.size()); + assertEquals(splitInfo0, splitInfoList.get(0)); + assertEquals(splitInfo1, splitInfoList.get(1)); + + // When any SplitContainer is in an intermediate state, getSplitStatesIfStable() returns + // null. + doReturn(null).when(splitContainer0).toSplitInfoIfStable(); + + splitInfoList = taskContainer.getSplitStatesIfStable(); + + assertNull(splitInfoList); + } } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java index 78b85e642c13..cc00a49604ee 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java @@ -321,6 +321,32 @@ public class TaskFragmentContainerTest { } @Test + public void testCollectNonFinishingActivities_checkIfStable() { + final TaskContainer taskContainer = createTestTaskContainer(); + final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); + + // In case mInfo is null, collectNonFinishingActivities(true) should return null. + List<Activity> activities = + container.collectNonFinishingActivities(true /* checkIfStable */); + assertNull(activities); + + // collectNonFinishingActivities(true) should return proper value when the container is in a + // stable state. + final List<IBinder> runningActivities = Lists.newArrayList(mActivity.getActivityToken()); + doReturn(runningActivities).when(mInfo).getActivities(); + container.setInfo(mTransaction, mInfo); + activities = container.collectNonFinishingActivities(true /* checkIfStable */); + assertEquals(1, activities.size()); + + // In case any activity is finishing, collectNonFinishingActivities(true) should return + // null. + doReturn(true).when(mActivity).isFinishing(); + activities = container.collectNonFinishingActivities(true /* checkIfStable */); + assertNull(activities); + } + + @Test public void testAddPendingActivity() { final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index e9abc7e522d5..187964947b94 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -151,6 +151,7 @@ android_library { static_libs: [ "androidx.appcompat_appcompat", "androidx.core_core-animation", + "androidx.core_core-ktx", "androidx.arch.core_core-runtime", "androidx-constraintlayout_constraintlayout", "androidx.dynamicanimation_dynamicanimation", @@ -158,6 +159,7 @@ android_library { "kotlinx-coroutines-android", "kotlinx-coroutines-core", "iconloader_base", + "com_android_wm_shell_flags_lib", "WindowManager-Shell-proto", "dagger2", "jsr330", diff --git a/libs/WindowManager/Shell/aconfig/Android.bp b/libs/WindowManager/Shell/aconfig/Android.bp new file mode 100644 index 000000000000..1a98ffcea9e7 --- /dev/null +++ b/libs/WindowManager/Shell/aconfig/Android.bp @@ -0,0 +1,12 @@ +aconfig_declarations { + name: "com_android_wm_shell_flags", + package: "com.android.wm.shell", + srcs: [ + "multitasking.aconfig", + ], +} + +java_aconfig_library { + name: "com_android_wm_shell_flags_lib", + aconfig_declarations: "com_android_wm_shell_flags", +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig new file mode 100644 index 000000000000..7c0d0e37e28c --- /dev/null +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -0,0 +1,22 @@ +package: "com.android.wm.shell" + +flag { + name: "example_flag" + namespace: "multitasking" + description: "An Example Flag" + bug: "300136750" +} + +flag { + name: "enable_app_pairs" + namespace: "multitasking" + description: "Enables the ability to create and save app pairs to the Home screen" + bug: "274835596" +} + +flag { + name: "desktop_windowing" + namespace: "multitasking" + description: "Enables desktop windowing" + bug: "304778354" +} diff --git a/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml new file mode 100644 index 000000000000..65f5239737b2 --- /dev/null +++ b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" + android:color="@color/desktop_mode_maximize_menu_button_on_hover"/> + <item android:state_hovered="true" + android:color="@color/desktop_mode_maximize_menu_button_on_hover"/> + <item android:state_focused="true" + android:color="@color/desktop_mode_maximize_menu_button_on_hover"/> + <item android:state_selected="true" + android:color="@color/desktop_mode_maximize_menu_button_on_hover"/> + <item android:color="@color/desktop_mode_maximize_menu_button"/> +</selector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_outline_color_selector.xml b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_outline_color_selector.xml new file mode 100644 index 000000000000..86679af5428b --- /dev/null +++ b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_outline_color_selector.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" + android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/> + <item android:state_hovered="true" + android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/> + <item android:state_focused="true" + android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/> + <item android:state_selected="true" + android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/> + <item android:color="@color/desktop_mode_maximize_menu_button_outline"/> +</selector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml index 4ee10f429b37..15837adc2c77 100644 --- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml +++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml @@ -15,7 +15,8 @@ ~ limitations under the License. --> <shape android:shape="rectangle" - xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="@android:color/white" /> + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> <corners android:radius="@dimen/desktop_mode_handle_menu_corner_radius" /> + <solid android:color="?androidprv:attr/materialColorSurfaceBright" /> </shape> diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml new file mode 100644 index 000000000000..5d9fe67e8bee --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<shape android:shape="rectangle" + xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@android:color/white" /> + <corners android:radius="@dimen/desktop_mode_maximize_menu_corner_radius" /> +</shape> diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_maximize_button_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_maximize_button_background.xml new file mode 100644 index 000000000000..bfb0dd7f3100 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_maximize_button_background.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License +--> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/desktop_mode_maximize_menu_button_color_selector"/> + <corners + android:radius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"/> + <stroke android:width="1dp" android:color="@color/desktop_mode_maximize_menu_button_outline_color_selector"/> +</shape>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_left_button_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_left_button_background.xml new file mode 100644 index 000000000000..6630fcab4794 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_left_button_background.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License +--> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/desktop_mode_maximize_menu_button_color_selector"/> + <corners + android:topLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius" + android:topRightRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius" + android:bottomLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius" + android:bottomRightRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius"/> + <stroke android:width="1dp" android:color="@color/desktop_mode_maximize_menu_button_outline_color_selector"/> +</shape>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_right_button_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_right_button_background.xml new file mode 100644 index 000000000000..7bd6e9981c12 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_right_button_background.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License +--> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/desktop_mode_maximize_menu_button_color_selector"/> + <corners + android:topLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius" + android:topRightRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius" + android:bottomLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius" + android:bottomRightRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"/> + <stroke android:width="1dp" android:color="@color/desktop_mode_maximize_menu_button_outline_color_selector"/> +</shape>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml b/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml index 3e0297ab612b..e9df936c3f94 100644 --- a/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml +++ b/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml @@ -14,8 +14,14 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<vector android:height="24dp" android:tint="#000000" - android:viewportHeight="24" android:viewportWidth="24" - android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/black" android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?android:attr/textColorSecondary"> + <path + android:fillColor="#FF000000" + android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17"/> </vector> + diff --git a/libs/WindowManager/Shell/res/drawable/ic_floating_landscape.xml b/libs/WindowManager/Shell/res/drawable/ic_floating_landscape.xml new file mode 100644 index 000000000000..8ef3307ee875 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/ic_floating_landscape.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M4,18H20V6H4V18ZM22,18C22,19.1 21.1,20 20,20H4C2.9,20 2,19.1 2,18V6C2,4.9 2.9,4 4,4H20C21.1,4 22,4.9 22,6V18ZM13,8H18V14H13V8Z" + android:fillColor="#455A64" + android:fillType="evenOdd"/> +</vector> diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml new file mode 100644 index 000000000000..b489a5c1acd0 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<com.android.wm.shell.common.bubbles.BubblePopupView + xmlns:android="http://schemas.android.com/apk/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:gravity="center_horizontal" + android:orientation="vertical"> + + <ImageView + android:layout_width="32dp" + android:layout_height="32dp" + android:tint="?android:attr/colorAccent" + 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: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:text="@string/bubble_bar_education_stack_title"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:maxWidth="@dimen/bubble_popup_content_max_width" + android:textAppearance="@android:style/TextAppearance.DeviceDefault" + android:textColor="?android:attr/textColorSecondary" + android:textAlignment="center" + android:text="@string/bubble_bar_education_stack_text"/> + +</com.android.wm.shell.common.bubbles.BubblePopupView>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml index 7e0c2071dc86..fa56516e0d22 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml @@ -31,35 +31,35 @@ android:orientation="horizontal" android:clickable="true" android:focusable="true" - android:paddingStart="8dp"> + android:paddingStart="16dp"> <ImageView android:id="@+id/application_icon" android:layout_width="24dp" android:layout_height="24dp" - android:layout_margin="4dp" android:layout_gravity="center_vertical" android:contentDescription="@string/app_icon_text" /> <TextView android:id="@+id/application_name" android:layout_width="0dp" - android:layout_height="match_parent" + android:layout_height="20dp" android:minWidth="80dp" android:textColor="@color/desktop_mode_caption_app_name_dark" + android:textAppearance="@android:style/TextAppearance.Material.Title" android:textSize="14sp" android:textFontWeight="500" - android:gravity="center_vertical" + android:lineHeight="20dp" + android:layout_gravity="center_vertical" android:layout_weight="1" - android:paddingStart="4dp" - android:paddingEnd="4dp" + android:paddingStart="8dp" + android:paddingEnd="8dp" tools:text="Gmail"/> <ImageButton android:id="@+id/expand_menu_button" - android:layout_width="32dp" - android:layout_height="32dp" - android:padding="4dp" + android:layout_width="16dp" + android:layout_height="16dp" android:contentDescription="@string/expand_menu_text" android:src="@drawable/ic_baseline_expand_more_24" android:tint="@color/desktop_mode_caption_expand_button_dark" diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml index d93e9ba32105..7638132d6562 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml @@ -26,8 +26,8 @@ <ImageButton android:id="@+id/caption_handle" android:layout_width="128dp" - android:layout_height="42dp" - android:paddingVertical="19dp" + android:layout_height="@dimen/desktop_mode_fullscreen_decor_caption_height" + android:paddingVertical="16dp" android:contentDescription="@string/handle_text" android:src="@drawable/decor_handle_dark" tools:tint="@color/desktop_mode_caption_handle_bar_dark" diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml new file mode 100644 index 000000000000..ee9f070f6765 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="@dimen/desktop_mode_handle_menu_width" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height" + android:layout_marginTop="@dimen/desktop_mode_handle_menu_margin_top" + android:layout_marginStart="1dp" + android:elevation="1dp" + android:orientation="horizontal" + android:background="@drawable/desktop_mode_decor_handle_menu_background" + android:gravity="center_vertical"> + + <ImageView + android:id="@+id/application_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="14dp" + android:layout_marginEnd="14dp" + android:contentDescription="@string/app_icon_text"/> + + <TextView + android:id="@+id/application_name" + android:layout_width="0dp" + android:layout_height="wrap_content" + tools:text="Gmail" + android:textColor="?androidprv:attr/materialColorOnSurface" + android:textSize="14sp" + android:textFontWeight="500" + android:lineHeight="20dp" + android:textStyle="normal" + android:layout_weight="1"/> + + <ImageButton + android:id="@+id/collapse_menu_button" + android:layout_width="32dp" + android:layout_height="32dp" + android:padding="4dp" + android:layout_marginEnd="14dp" + android:layout_marginStart="14dp" + android:contentDescription="@string/collapse_menu_text" + android:src="@drawable/ic_baseline_expand_more_24" + android:rotation="180" + android:tint="?androidprv:attr/materialColorOnSurface" + android:background="?android:selectableItemBackgroundBorderless"/> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height" + android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin" + android:layout_marginStart="1dp" + android:orientation="horizontal" + android:elevation="1dp" + android:background="@drawable/desktop_mode_decor_handle_menu_background" + android:gravity="center_vertical"> + + <ImageButton + android:id="@+id/fullscreen_button" + android:layout_marginEnd="4dp" + android:contentDescription="@string/fullscreen_text" + android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen" + android:tint="?androidprv:attr/materialColorOnSurface" + android:layout_weight="1" + style="@style/DesktopModeHandleMenuWindowingButton"/> + + <ImageButton + android:id="@+id/split_screen_button" + android:layout_marginStart="4dp" + android:layout_marginEnd="4dp" + android:contentDescription="@string/split_screen_text" + android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen" + android:tint="?androidprv:attr/materialColorOnSurface" + android:layout_weight="1" + style="@style/DesktopModeHandleMenuWindowingButton"/> + + <ImageButton + android:id="@+id/floating_button" + android:layout_marginStart="4dp" + android:layout_marginEnd="4dp" + android:contentDescription="@string/float_button_text" + android:src="@drawable/desktop_mode_ic_handle_menu_floating" + android:tint="?androidprv:attr/materialColorOnSurface" + android:layout_weight="1" + style="@style/DesktopModeHandleMenuWindowingButton"/> + + <ImageButton + android:id="@+id/desktop_button" + android:layout_marginStart="4dp" + android:contentDescription="@string/desktop_text" + android:src="@drawable/desktop_mode_ic_handle_menu_desktop" + android:tint="?androidprv:attr/materialColorOnSurface" + android:layout_weight="1" + style="@style/DesktopModeHandleMenuWindowingButton"/> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height" + android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin" + android:layout_marginStart="1dp" + android:orientation="vertical" + android:elevation="1dp" + android:background="@drawable/desktop_mode_decor_handle_menu_background"> + + <Button + android:id="@+id/screenshot_button" + android:contentDescription="@string/screenshot_text" + android:text="@string/screenshot_text" + android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot" + android:drawableTint="?androidprv:attr/materialColorOnSurface" + style="@style/DesktopModeHandleMenuActionButton"/> + + <Button + android:id="@+id/select_button" + android:contentDescription="@string/select_text" + android:text="@string/select_text" + android:drawableStart="@drawable/desktop_mode_ic_handle_menu_select" + android:drawableTint="?androidprv:attr/materialColorOnSurface" + style="@style/DesktopModeHandleMenuActionButton"/> + + </LinearLayout> +</LinearLayout> + diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml deleted file mode 100644 index 167a003932d6..000000000000 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="@dimen/desktop_mode_handle_menu_width" - android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height" - android:orientation="horizontal" - android:background="@drawable/desktop_mode_decor_menu_background" - android:gravity="center_vertical"> - - <ImageView - android:id="@+id/application_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_marginStart="14dp" - android:layout_marginEnd="14dp" - android:contentDescription="@string/app_icon_text"/> - - <TextView - android:id="@+id/application_name" - android:layout_width="0dp" - android:layout_height="wrap_content" - tools:text="Gmail" - android:textColor="@color/desktop_mode_caption_menu_text_color" - android:textSize="14sp" - android:textFontWeight="500" - android:lineHeight="20dp" - android:textStyle="normal" - android:layout_weight="1"/> - - <ImageButton - android:id="@+id/collapse_menu_button" - android:layout_width="32dp" - android:layout_height="32dp" - android:padding="4dp" - android:layout_marginEnd="14dp" - android:layout_marginStart="14dp" - android:contentDescription="@string/collapse_menu_text" - android:src="@drawable/ic_baseline_expand_more_24" - android:rotation="180" - android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive" - android:background="?android:selectableItemBackgroundBorderless"/> -</LinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml deleted file mode 100644 index 40a4b53f3e1d..000000000000 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="@dimen/desktop_mode_handle_menu_width" - android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height" - android:orientation="vertical" - android:background="@drawable/desktop_mode_decor_menu_background"> - - <Button - android:id="@+id/screenshot_button" - android:contentDescription="@string/screenshot_text" - android:text="@string/screenshot_text" - android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot" - android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive" - style="@style/DesktopModeHandleMenuActionButton"/> - - <Button - android:id="@+id/select_button" - android:contentDescription="@string/select_text" - android:text="@string/select_text" - android:drawableStart="@drawable/desktop_mode_ic_handle_menu_select" - android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive" - style="@style/DesktopModeHandleMenuActionButton"/> - - <Button - android:id="@+id/close_button" - android:contentDescription="@string/close_text" - android:text="@string/close_text" - android:drawableStart="@drawable/desktop_mode_ic_handle_menu_close" - android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive" - style="@style/DesktopModeHandleMenuActionButton"/> - -</LinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml deleted file mode 100644 index 95283b9e214a..000000000000 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml +++ /dev/null @@ -1,62 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="@dimen/desktop_mode_handle_menu_width" - android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height" - android:orientation="horizontal" - android:background="@drawable/desktop_mode_decor_menu_background" - android:gravity="center_vertical"> - - <ImageButton - android:id="@+id/fullscreen_button" - android:layout_marginEnd="4dp" - android:contentDescription="@string/fullscreen_text" - android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen" - android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive" - android:layout_weight="1" - style="@style/DesktopModeHandleMenuWindowingButton"/> - - <ImageButton - android:id="@+id/split_screen_button" - android:layout_marginStart="4dp" - android:layout_marginEnd="4dp" - android:contentDescription="@string/split_screen_text" - android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen" - android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive" - android:layout_weight="1" - style="@style/DesktopModeHandleMenuWindowingButton"/> - - <ImageButton - android:id="@+id/floating_button" - android:layout_marginStart="4dp" - android:layout_marginEnd="4dp" - android:contentDescription="@string/float_button_text" - android:src="@drawable/desktop_mode_ic_handle_menu_floating" - android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive" - android:layout_weight="1" - style="@style/DesktopModeHandleMenuWindowingButton"/> - - <ImageButton - android:id="@+id/desktop_button" - android:layout_marginStart="4dp" - android:contentDescription="@string/desktop_text" - android:src="@drawable/desktop_mode_ic_handle_menu_desktop" - android:tint="@color/desktop_mode_caption_menu_buttons_color_active" - android:layout_weight="1" - style="@style/DesktopModeHandleMenuWindowingButton"/> - -</LinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml new file mode 100644 index 000000000000..0db72f7be8e6 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + style="?android:attr/buttonBarStyle" + android:layout_width="@dimen/desktop_mode_maximize_menu_width" + android:layout_height="@dimen/desktop_mode_maximize_menu_height" + android:orientation="horizontal" + android:gravity="center" + android:background="@drawable/desktop_mode_maximize_menu_background"> + + + <Button + android:id="@+id/maximize_menu_maximize_button" + style="?android:attr/buttonBarButtonStyle" + android:layout_width="120dp" + android:layout_height="80dp" + android:layout_marginRight="15dp" + android:color="@color/desktop_mode_maximize_menu_button" + android:background="@drawable/desktop_mode_maximize_menu_maximize_button_background" + android:stateListAnimator="@null"/> + + <Button + android:id="@+id/maximize_menu_snap_left_button" + style="?android:attr/buttonBarButtonStyle" + android:layout_width="58dp" + android:layout_height="80dp" + android:layout_marginRight="6dp" + android:color="@color/desktop_mode_maximize_menu_button" + android:background="@drawable/desktop_mode_maximize_menu_snap_left_button_background" + android:stateListAnimator="@null"/> + + <Button + android:id="@+id/maximize_menu_snap_right_button" + style="?android:attr/buttonBarButtonStyle" + android:layout_width="58dp" + android:layout_height="80dp" + android:color="@color/desktop_mode_maximize_menu_button" + android:background="@drawable/desktop_mode_maximize_menu_snap_right_button_background" + android:stateListAnimator="@null"/> +</LinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml index de4a225e41a7..202ea957971e 100644 --- a/libs/WindowManager/Shell/res/values-af/strings.xml +++ b/libs/WindowManager/Shell/res/values-af/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Beweeg na regs bo"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Beweeg na links onder"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Beweeg na regs onder"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"vou <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> uit"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"vou <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> in"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-instellings"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Maak borrel toe"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Moenie dat gesprek \'n borrel word nie"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Het dit"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen onlangse borrels nie"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Onlangse borrels en borrels wat toegemaak is, sal hier verskyn"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Klets met borrels"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nuwe gesprekke verskyn as ikone in ān hoek onderaan jou skerm. Tik om hulle uit te vou, of sleep om hulle toe te maak."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Beheer borrels enige tyd"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tik hier om te bestuur watter apps en gesprekke in borrels kan verskyn"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Tik om hierdie program te herbegin vir ān beter aansig."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Tik om hierdie app te herbegin vir ān beter aansig"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Verander hierdie app se aspekverhouding in Instellings"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Verander aspekverhouding"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerakwessies?\nTik om aan te pas"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string> diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml index 21172e2267bc..4071e790d55a 100644 --- a/libs/WindowManager/Shell/res/values-am/strings.xml +++ b/libs/WindowManager/Shell/res/values-am/strings.xml @@ -32,23 +32,23 @@ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"įį į įįįį©"</string> <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string> <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string> - <string name="dock_forced_resizable" msgid="7429086980048964687">"įį°įį įŖį« įØį°įØįį įį« įį½ įį įįį į« įį½įį"</string> + <string name="dock_forced_resizable" msgid="7429086980048964687">"įį°įį įŖį« įØį°įØįį įį« įį
įį įįį į« įį½įį"</string> <string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"įį°įį įŖį«į įØį°įØįį įį« įį½į į įį°įįį"</string> <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"įį
įį°įį įŖį« įįØįįµ įØįį½įį į 1 įįµį®įµ į„į» įįį¢"</string> <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"įį°įį įŖį« į įįį°į įį³į« įį įįį į« įį½įįį¢"</string> <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"įį°įį įŖį« į įįį°į įį³į«įį½ įį įįµįįįį į įį°įįįį¢"</string> - <string name="accessibility_divider" msgid="6407584574218956849">"įØį°įØįį įØįį« įį½ įØįį"</string> - <string name="divider_title" msgid="1963391955593749442">"įØį°įØįį įØįį« įį½ įØįį"</string> - <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"įØįį« įį įį« įį½"</string> + <string name="accessibility_divider" msgid="6407584574218956849">"įØį°įØįį įØįį« įį
įØįį"</string> + <string name="divider_title" msgid="1963391955593749442">"įØį°įØįį įØįį« įį
įØįį"</string> + <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"įØįį« įį įį« įį
"</string> <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"įį« 70%"</string> <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"įį« 50%"</string> <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"įį« 30%"</string> - <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"įØįį įį įį« įį½"</string> - <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"įØįį įį įį« įį½"</string> + <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"įØįį įį įį« įį
"</string> + <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"įØįį įį įį« įį
"</string> <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"įØįį 70%"</string> <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"įØįį 50%"</string> <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"įØįį 30%"</string> - <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"įØį³į½ įį įį« įį½"</string> + <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"įØį³į½ įį įį« įį
"</string> <string name="accessibility_split_left" msgid="1713683765575562458">"įį° įį« įØįįį"</string> <string name="accessibility_split_right" msgid="8441001008181296837">"įį° įį įØįįį"</string> <string name="accessibility_split_top" msgid="2789329702027147146">"įį° įį įØįįį"</string> @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"įį° įįįį įį į įįį³į
įµ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"įØįįįįį įį« į įįį³į
įµ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"į³į½įįį įį į«įįį³į
į±"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>į įįį"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>į į°į„įµį„"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"įØ<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> į
įį„į®į½"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"į įØįį į į°įį„įµ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"įįįį¶į½į į į įØį į į³į³į"</string> @@ -76,16 +78,22 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"įį£į"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"įįį įØį
įį„ įį į įØįįį½ įØįį"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"įØį
įį„ įį į įØįįį½ į„į įØį°į°įį į± į įØįįį½ į„įį
į„į
įįį"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"į įØįįį½į į įį įį įįį«į©"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"į į²įµ įįįį¶į½ į įį« įį
į įØį³į½įį į„į įįµį„ į„įį° į į¶įį½ įį³į«įᢠįįįįįįµ įį³ į«įµįįįøį įįį įįį°įį įµ įįįµį·įøįį¢"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"į įįįįį įį įįØįįį½į įįį£į į©"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"įØįµįįį¹ įį°įį įŖį«įį½ į„į įįįį¶į½ įįØį įįį į į„įį°įį½į įįįµį°į³į°į į„įį
įį įį³ į«įµįį"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"į įØį"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"į«įįį„į©"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"į įØį į°į°įį„į·įį¢"</string> - <string name="restart_button_description" msgid="6712141648865547958">"įį°į»į įįį³ įį
į įį°įį įŖį« į³įį įįįµįį³įµ įį³ į«įµįįį¢"</string> + <string name="restart_button_description" msgid="4564728020654658478">"įį°į»į įįį³ įį
į įį°įį įŖį« į„įį°įį įįįįį įį³ į«įµįį"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"įØįį
į įį°įį įŖį« įį„į„į įį
į³ į į
įį„į®į½ įįµį„ įįįį”"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"įØįį„į„į įį
į³ įįį„"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"įØį«įį« į½įį®į½ į į?\nį³įį įįį įįµ įį³ į«įµįį"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"į įį°įµį°į«įØįį?\nįįį
į°į įį³ į«įµįį"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"įįį įØį«įį« į½įį®į½ įØįį? įįį°įį įµ įį³ į«įµįįį¢"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"į°įØįįŖ įįįįØį± į„į į«įµįį"</string> - <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"įį°įØįį įį« įį½ įį įį°įį įŖį« įįįµį±"</string> - <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"į¦į³įį įįįįØį įØįį°įį įŖį«į įįŖ įįį“ įį³ į«įµįį"</string> + <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"įį°įØįį įį« įį
įį įį°įį įŖį« įįįµį±"</string> + <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"į¦į³įį įįįįØį įØįį°įį įŖį«į įį įįį“ įį³ į«įµįį"</string> <string name="letterbox_education_got_it" msgid="4057634570866051177">"įį£į"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"įį°įØįįŖ įįØį įįįįį¢"</string> <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"įį°į»į įįį³ į„įį°įį įįįį?"</string> @@ -102,11 +110,11 @@ <string name="app_icon_text" msgid="2823268023931811747">"įØįį°įį įŖį« į į¶"</string> <string name="fullscreen_text" msgid="1162316685217676079">"įį įį«"</string> <string name="desktop_text" msgid="1077633567027630454">"įØį“įµįį¶į įįį³"</string> - <string name="split_screen_text" msgid="1396336058129570886">"įØį°įØįį įį« įį½"</string> + <string name="split_screen_text" msgid="1396336058129570886">"įØį°įØįį įį« įį
"</string> <string name="more_button_text" msgid="3655388105592893530">"į°įØįįŖ"</string> <string name="float_button_text" msgid="9221657008391364581">"į°įį³įį"</string> <string name="select_text" msgid="5139083974039906583">"įįØį„"</string> - <string name="screenshot_text" msgid="1477704010087786671">"į
į½į į³į įį½ į„įį³"</string> + <string name="screenshot_text" msgid="1477704010087786671">"į
į½į į³į įį
įįį³"</string> <string name="close_text" msgid="4986518933445178928">"įį"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"įįį įį"</string> <string name="expand_menu_text" msgid="3847736164494181168">"įįįį įįįµ"</string> diff --git a/libs/WindowManager/Shell/res/values-am/strings_tv.xml b/libs/WindowManager/Shell/res/values-am/strings_tv.xml index a6be57889a4e..84c1c6763d43 100644 --- a/libs/WindowManager/Shell/res/values-am/strings_tv.xml +++ b/libs/WindowManager/Shell/res/values-am/strings_tv.xml @@ -20,7 +20,7 @@ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"į„įį-įį-į„įį"</string> <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(įįįµ įØįįį įį®įį«į)"</string> <string name="pip_close" msgid="2955969519031223530">"įį"</string> - <string name="pip_fullscreen" msgid="7278047353591302554">"įį įį« įį½"</string> + <string name="pip_fullscreen" msgid="7278047353591302554">"įį įį« įį
"</string> <string name="pip_move" msgid="158770205886688553">"įį°įµ"</string> <string name="pip_expand" msgid="1051966011679297308">"įįį"</string> <string name="pip_collapse" msgid="3903295106641385962">"į°į„įµį„"</string> diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index a714b49d1305..d3890a779f87 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ų§ŁŲ§ŁŲŖŁŲ§Ł Ų„ŁŁ Ų£Ų¹ŁŁ Ų§ŁŁŲ³Ų§Ų±"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ŁŁŁ Ų„ŁŁ Ų£Ų³ŁŁ ŁŁ
ŁŁ Ų§ŁŲ“Ų§Ų“Ų©"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ŁŁŁ Ų„ŁŁ Ų£Ų³ŁŁ Ų§ŁŁŲ³Ų§Ų±"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ŲŖŁŲ³ŁŲ¹ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ŲŖŲµŲŗŁŲ± <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Ų„Ų¹ŲÆŲ§ŲÆŲ§ŲŖ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ų„ŲŗŁŲ§Ł ŁŁŲ§Ų¹Ų© Ų§ŁŁ
ŲŲ§ŲÆŲ«Ų©"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ų¹ŲÆŁ
Ų¹Ų±Ų¶ Ų§ŁŁ
ŲŲ§ŲÆŲ«Ų© ŁŁŁŲ§Ų¹Ų© Ł
ŲŲ§ŲÆŲ«Ų©"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ŲŲ³ŁŁŲ§"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ŁŁŲ³ ŁŁŲ§Ł ŁŁŲ§Ų¹Ų§ŲŖ Ł
ŲŲ§ŲÆŲ«Ų§ŲŖ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ų³ŲŖŲøŁŲ± ŁŁŲ§ Ų£ŲŲÆŲ« ŁŁŲ§Ų¹Ų§ŲŖ Ų§ŁŁ
ŲŲ§ŲÆŲ«Ų§ŲŖ ŁŁŁŲ§Ų¹Ų§ŲŖ Ų§ŁŁ
ŲŲ§ŲÆŲ«Ų§ŲŖ Ų§ŁŲŖŁ ŲŖŁ
Ų„ŲŗŁŲ§ŁŁŲ§."</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Ų§ŁŲÆŲ±ŲÆŲ“Ų© ŲØŲ§Ų³ŲŖŲ®ŲÆŲ§Ł
ŁŁŲ§Ų¹Ų§ŲŖ Ų§ŁŁ
ŲŲ§ŲÆŲ«Ų§ŲŖ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ŲŖŲøŁŲ± Ų§ŁŁ
ŲŲ§ŲÆŲ«Ų§ŲŖ Ų§ŁŲ¬ŲÆŁŲÆŲ© ŁŁ Ų“ŁŁ Ų±Ł
ŁŲ² ŲØŲ£Ų³ŁŁ Ų£ŲŲÆ Ų¬Ų§ŁŲØŁŁ Ų§ŁŲ“Ų§Ų“Ų©. Ų§ŁŁŲ± Ų¹ŁŁ Ų§ŁŲ±Ł
Ų² ŁŲŖŁŲ³ŁŲ¹ Ų§ŁŁ
ŲŲ§ŲÆŲ«Ų© أ٠اسŲŲØŁ ŁŲ„Ų®ŁŲ§Ų¦ŁŲ§."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Ų§ŁŲŖŲŁŁŁ
ŁŁ Ų„ŲøŁŲ§Ų± Ų§ŁŁŁŲ§Ų¹Ų§ŲŖ ŁŁ Ų£Ł ŁŁŲŖ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ų§ŁŁŲ± ŁŁŲ§ ŁŁŲŖŲŁŁŁ
ŁŁ Ų„ŲøŁŲ§Ų± ŁŁŲ§Ų¹Ų§ŲŖ Ų§ŁŲŖŲ·ŲØŁŁŲ§ŲŖ ŁŲ§ŁŁ
ŲŲ§ŲÆŲ«Ų§ŲŖ Ų§ŁŲŖŁ ŲŖŲ±ŁŲÆŁŲ§."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ŁŁŲ§Ų¹Ų©"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Ų„ŲÆŲ§Ų±Ų©"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ŲŖŁ
Ų„ŲŗŁŲ§Ł Ų§ŁŁŁŲ§Ų¹Ų©."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Ų§ŁŁŲ± ŁŲ„Ų¹Ų§ŲÆŲ© ŲŖŲ“ŲŗŁŁ ŁŲ°Ų§ Ų§ŁŲŖŲ·ŲØŁŁ ŁŁŲŲµŁŁ Ų¹ŁŁ Ų¹Ų±Ų¶ Ų£ŁŲ¶Ł."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Ų§ŁŁŲ± ŁŲ„Ų¹Ų§ŲÆŲ© ŲŖŲ“ŲŗŁŁ ŁŲ°Ų§ Ų§ŁŲŖŲ·ŲØŁŁ ŁŁŲŲµŁŁ Ų¹ŁŁ ŲŖŲ¬Ų±ŲØŲ© Ų¹Ų±Ų¶ Ų£ŁŲ¶Ł."</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŁŁ
ŁŁŁ ŲŖŲŗŁŁŲ± ŁŲ³ŲØŲ© Ų§ŁŲ¹Ų±Ų¶ Ų„ŁŁ Ų§ŁŲ§Ų±ŲŖŁŲ§Ų¹ ŁŁŲ°Ų§ Ų§ŁŲŖŲ·ŲØŁŁ Ł
Ł Ų®ŁŲ§Ł \"Ų§ŁŲ„Ų¹ŲÆŲ§ŲÆŲ§ŲŖ\"."</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ŲŖŲŗŁŁŲ± ŁŲ³ŲØŲ© Ų§ŁŲ¹Ų±Ų¶ Ų„ŁŁ Ų§ŁŲ§Ų±ŲŖŁŲ§Ų¹"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ŁŁ ŁŁŲ§Ł Ł
Ų“Ų§ŁŁ ŁŁ Ų§ŁŁŲ§Ł
ŁŲ±Ų§Ų\nŲ§ŁŁŲ± ŁŲ„Ų¹Ų§ŲÆŲ© Ų§ŁŲ¶ŲØŲ·."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ų£ŁŁ
ŁŲŖŁ
ŲŁ Ų§ŁŁ
Ų“ŁŁŲ©Ų\nŲ§ŁŁŲ± ŁŁŲ¹ŁŲÆŲ©"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ų£ŁŁŲ³ ŁŁŲ§Ł Ł
Ų“Ų§ŁŁ ŁŁ Ų§ŁŁŲ§Ł
ŁŲ±Ų§Ų Ų§ŁŁŲ± ŁŁŲ„ŲŗŁŲ§Ł."</string> diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml index 6d86747ef336..05b8f7dca729 100644 --- a/libs/WindowManager/Shell/res/values-as/strings.xml +++ b/libs/WindowManager/Shell/res/values-as/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ą¦¶ą§ą§°ą§ą¦·ą§° ą¦øą§ą¦ą¦«ą¦¾ą¦²ą§ নিয়ą¦"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ą¦¬ą§ą¦ą¦¾ą¦®ą¦ą§ ą¦¬ą¦¾ą¦ą¦ą¦«ą¦¾ą¦²ą§ নিয়ą¦"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"তলৰ ą¦øą§ą¦ą¦«ą¦¾ą¦²ą§ নিয়ą¦"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¦¬ą¦æą¦øą§ą¦¤ą¦¾ą§° ą¦ą§°ą¦"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¦øą¦ą¦ą§ą¦ą¦Ø ą¦ą§°ą¦"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ą¦ą§ą¦ą¦æą¦"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল ą¦
ą¦ą§ą§°ą¦¾ą¦¹ą§ą¦Æ ą¦ą§°ą¦"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą¦¬ą¦¾ą§°ą§ą¦¤ą¦¾ą¦²ą¦¾ą¦Ŗ বাবল ą¦Øą¦ą§°ą¦æą¦¬"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą¦¬ą§ą¦ą¦æ ą¦Ŗą¦¾ą¦²ą§ą¦"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ą¦ą§ą¦Øą§ ą¦¶ą§ą¦¹ą¦¤ą§ą¦Æą¦¼ą¦¾ bubbles নাą¦"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ą¦¶ą§ą¦¹ą¦¤ą§ą¦Æą¦¼ą¦¾ bubbles ą¦ą§°ą§ ą¦
ą¦ą§ą§°ą¦¾ą¦¹ą§ą¦Æ ą¦ą§°ą¦¾ bubbles ą¦ą¦Æą¦¼ą¦¾ą¦¤ ą¦Ŗą§ą§°ą¦¦ą¦°ą§ą¦¶ą¦æą¦¤ হ\'ব"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bubbles ą¦¬ą§ą¦Æą§±ą¦¹ą¦¾ą§° ą¦ą§°ą¦æ ą¦ą¦¾ą¦ ą¦ą§°ą¦"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ą¦Øą¦¤ą§ą¦Ø ą¦¬ą¦¾ą§°ą§ą¦¤ą¦¾ą¦²ą¦¾ą¦Ŗą¦øą¦®ą§ą¦¹ ą¦ą¦Ŗą§ą¦Øą¦¾ą§° ą¦øą§ą¦ą§ą§°ą§ą¦Øą§° ą¦ą¦ą§ą¦¬ą¦¾ą§°ą§ তলৰ ą¦ą¦ą¦¾ ą¦ą§ą¦£ą¦¤ ą¦ą¦æą¦¹ą§ą¦Ø ą¦¹ą¦æą¦ą¦¾ą¦Ŗą§ ą¦¦ą§ą¦ą¦¾ ą¦Ŗą§ą§±ą¦¾ ą¦Æą¦¾ą¦Æą¦¼ą„¤ ą¦øą§ą¦ą¦øą¦®ą§ą¦¹ ą¦¬ą¦æą¦øą§ą¦¤ą¦¾ą§° ą¦ą§°ą¦æą¦¬ą¦²ą§ ą¦ą¦æą¦Ŗą¦ বা ą¦
ą¦ą§ą§°ą¦¾ą¦¹ą§ą¦Æ ą¦ą§°ą¦æą¦¬ą¦²ą§ ą¦ą¦¾ą¦Øą¦æ ą¦ą¦Øą¦æ ą¦ą§°ą¦ą„¤"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ą¦Æą¦æą¦ą§ą¦Øą§ সময়তৠবাবল ą¦Øą¦æą¦Æą¦¼ą¦Øą§ą¦¤ą§ą§°ą¦£ ą¦ą§°ą¦"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ą¦ą§ą¦Øą¦¬ą§ą§° ą¦ą¦Ŗą§ā ą¦ą§°ą§ ą¦¬ą¦¾ą§°ą§ą¦¤ą¦¾ą¦²ą¦¾ą¦Ŗ বাবল ą¦¹āą¦¬ ą¦Ŗą¦¾ą§°ą§ ą¦øą§ą¦Æą¦¼ą¦¾ ą¦Ŗą§°ą¦æą¦ą¦¾ą¦²ą¦Øą¦¾ ą¦ą§°ą¦æą¦¬ą¦²ą§ ą¦ą¦Æą¦¼ą¦¾ą¦¤ ą¦ą¦æą¦Ŗą¦"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ą¦Ŗą§°ą¦æą¦ą¦¾ą¦²ą¦Øą¦¾ ą¦ą§°ą¦"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল ą¦
ą¦ą§ą§°ą¦¾ą¦¹ą§ą¦Æ ą¦ą§°ą¦¾ ą¦¹ą§ą¦ą§"</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą¦ą¦Øą§ą¦Øą¦¤ ą¦ą¦æą¦ą§° ą¦¬ą¦¾ą¦¬ą§ ą¦ą¦Ŗą§āą¦ą§ ą§°ą¦æą¦·ą§ą¦ą¦¾ą§°ą§ą¦ ą¦ą§°ą¦æą¦¬ą¦²ą§ ą¦ą¦æą¦Ŗą¦ą„¤"</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą¦ą¦Øą§ą¦Øą¦¤ ą¦ą¦æą¦ ą¦Ŗą§ą§±ą¦¾ą§° ą¦¬ą¦¾ą¦¬ą§ ą¦ą¦Ŗą§āą¦ą§ ą§°ą¦æą¦·ą§ą¦ą¦¾ą§°ą§ą¦ ą¦ą§°ą¦æą¦¬ą¦²ą§ ą¦ą¦æą¦Ŗą¦"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą¦ą§ą¦ą¦æą¦ą¦²ą§ ą¦ą§ ą¦ą¦ ą¦ą¦Ŗą§āą¦ą§ą§° ą¦ą¦ą¦¾ą§°ą§° ą¦
ą¦Øą§ą¦Ŗą¦¾ą¦¤ সলনি ą¦ą§°ą¦"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą¦ą¦ą¦¾ą§°ą§° ą¦
ą¦Øą§ą¦Ŗą¦¾ą¦¤ সলনি ą¦ą§°ą¦"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ą¦ą§ą¦®ą§ą§°ą¦¾ą§° ą¦ą§ą¦Øą§ ą¦øą¦®ą¦øą§ą¦Æą¦¾ ą¦¹ą§ą¦ą§ ą¦Øą§ą¦ą¦æ?\ną¦Ŗą§ą¦Øą§° ą¦ą¦¾ą¦Ŗ ą¦ą§ą§±ą¦¾ą¦¬ą¦²ą§ ą¦ą¦æą¦Ŗą¦"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ą¦ą¦ą¦ą§ সমাধান ą¦ą§°ą¦¾ ą¦Øą¦¾ą¦ ą¦Øą§ą¦ą¦æ?\ną¦Ŗą§ą§°ą§ą¦¬ą¦¾ą§±ą¦øą§ą¦„ą¦¾ą¦²ą§ ą¦Øą¦æą¦¬ą¦²ą§ ą¦ą¦æą¦Ŗą¦"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ą¦ą§ą¦®ą§ą§°ą¦¾ą§° ą¦ą§ą¦Øą§ ą¦øą¦®ą¦øą§ą¦Æą¦¾ ą¦Øą¦¾ą¦ ą¦Øą§ą¦ą¦æ? ą¦
ą¦ą§ą§°ą¦¾ą¦¹ą§ą¦Æ ą¦ą§°ą¦æą¦¬ą¦²ą§ ą¦ą¦æą¦Ŗą¦ą„¤"</string> diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml index 7c662827cca7..108593e4b948 100644 --- a/libs/WindowManager/Shell/res/values-az/strings.xml +++ b/libs/WindowManager/Shell/res/values-az/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuxarıya saÄa köçürün"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"AÅaÄıya sola köçürün"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"AÅaÄıya saÄa köçürün"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"geniÅlÉndirin: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"yıÄcamlaÅdırın: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"YumrucuÄu lÉÄv edin"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"SƶhbÉti yumrucuqda gƶstÉrmÉ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Anladım"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Yumrucuqlar yoxdur"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son yumrucuqlar vÉ buraxılmıŠyumrucuqlar burada gƶrünÉcÉk"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Yumrucuqlar vasitÉsilÉ sƶhbÉt edin"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Yeni sƶhbÉtlÉr ekranın aÅaÄı küncündÉ ikonalar kimi gƶrünür. Toxunaraq geniÅlÉndirin, yaxud sürüÅdürÉrÉk imtina edin."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Yumrucuqları idarÉ edin"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Bura toxunaraq yumrucuq gƶstÉrÉcÉk tÉtbiq vÉ sƶhbÉtlÉri idarÉ edin"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"İdarÉ edin"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Toxunaraq bu tÉtbiqi yenidÉn baÅladın ki, daha gƶrüntü ÉldÉ edÉsiniz."</string> + <string name="restart_button_description" msgid="4564728020654658478">"YaxÅı gƶrünüŠüçün toxunaraq bu tÉtbiqi yenidÉn baÅladın"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ayarlarda bu tÉtbiqin tÉrÉflÉr nisbÉtini dÉyiÅin"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"TÉrÉflÉr nisbÉtini dÉyiÅin"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera problemi var?\nBÉrpa etmÉk üçün toxunun"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"DüzÉltmÉmisiniz?\nGeri qaytarmaq üçün toxunun"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</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 8de9d11def2b..76fd5b1c2d67 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premesti gore desno"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premesti dole levo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premesti dole desno"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proÅ”irite oblaÄiÄ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skupite oblaÄiÄ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"PodeÅ”avanja za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblaÄiÄ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne koristi oblaÄiÄe za konverzaciju"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Važi"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblaÄiÄa"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovde se prikazuju nedavni i odbaÄeni oblaÄiÄi"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Äaskajte u oblaÄiÄima"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nove konverzacije se pojavljuju kao ikone u donjem uglu ekrana. Dodirnite da biste ih proÅ”irili ili prevucite da biste ih odbacili."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"KontroliÅ”ite oblaÄiÄe u svakom trenutku"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dodirnite ovde i odredite koje aplikacije i konverzacije mogu da imaju oblaÄiÄ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"OblaÄiÄ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"OblaÄiÄ je odbaÄen."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da biste restartovali ovu aplikaciju radi boljeg prikaza."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da biste restartovali ovu aplikaciju radi boljeg prikaza"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promenite razmeru ove aplikacije u PodeÅ”avanjima"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promeni razmeru"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Imate problema sa kamerom?\nDodirnite da biste ponovo uklopili"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije reÅ”en?\nDodirnite da biste vratili"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string> @@ -89,7 +97,7 @@ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Važi"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ProÅ”irite za joÅ” informacija."</string> <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Želite li da restartujete radi boljeg prikaza?"</string> - <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Možete da restartujete aplikaciju da bi izgledala bolje na ekranu, s tim Å”to možete da izgubite ono Å”to ste uradili ili nesaÄuvane promene, ako ih ima"</string> + <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Možete da restartujete aplikaciju da bi izgledala bolje na ekranu, ali možete da izgubite napredak ili nesaÄuvane promene"</string> <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Otkaži"</string> <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restartuj"</string> <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne prikazuj ponovo"</string> diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml index 3d99514e2172..473d15acc632 100644 --- a/libs/WindowManager/Shell/res/values-be/strings.xml +++ b/libs/WindowManager/Shell/res/values-be/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ŠŠµŃамŃŃŃŃŃŠµ ŠæŃŠ°Š²ŠµŠ¹ Ń Š²ŃŃŃŠ¹"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ŠŠµŃамŃŃŃŃŃŃ Š»ŃŠ²ŠµŠ¹ Ń Š½ŃŠ¶Ńй"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ŠŠµŃамŃŃŃŃŃŃ ŠæŃŠ°Š²ŠµŠ¹ Ń Š½ŃŠ¶Ńй"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: ŃŠ°Š·Š³Š°ŃнŃŃŃ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: Š·Š³Š°ŃŠ½ŃŃŃ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ŠŠ°Š»Š°Š“Ń \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ŠŠ“Ń
ŃŠ»ŃŃŃ Š°ŠæŠ°Š²ŃŃŃŃŠ½Š½Šµ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ŠŠµ паказваŃŃ ŃŠ°Š·Š¼Š¾Š²Ń Ń Š²ŃŠ³Š»ŃГзе ŃŃŠæŠ»ŃŠ²Š°Š»ŃнŃŃ
апавŃŃŃŃŠ½Š½ŃŃ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ŠŃŠ°Š·ŃŠ¼ŠµŠ»Š°"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ŠŃма Š½ŃŠ“Š°ŃŠ½ŃŃ
ŃŃŠæŠ»ŃŠ²Š°Š»ŃнŃŃ
апавŃŃŃŃŠ½Š½ŃŃ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ŠŃŠ“Š°ŃŠ½ŃŃ Ń Š°Š“Ń
ŃŠ»ŠµŠ½ŃŃ ŃŃŠæŠ»ŃŠ²Š°Š»ŃнŃŃ Š°ŠæŠ°Š²ŃŃŃŃŠ½Š½Ń Š±ŃŠ“ŃŃŃ ŠæŠ°ŠŗŠ°Š·Š°Š½Ń ŃŃŃ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Š§Š°Ń Š· Š²ŃŠŗŠ°ŃŃŃŃŠ°Š½Š½ŠµŠ¼ ŃŃŠæŠ»ŃŠ²Š°Š»ŃнŃŃ
апавŃŃŃŃŠ½Š½ŃŃ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ŠŠ¾Š²ŃŃ ŃŠ°Š·Š¼Š¾Š²Ń паказваŃŃŃŠ° Ń Š²ŃŠ³Š»ŃГзе Š·Š½Š°ŃŠŗŠ¾Ń Ń Š½ŃŠ¶Š½Ńм Š²Ńгле ŃŠŗŃана. ŠŠ°ŃŃŃŠ½ŃŃŠµ на ŃŃ
, каб ŃŠ°Š·Š³Š°ŃнŃŃŃ. ŠŠµŃаŃŃŠ³Š½ŃŃŠµ ŃŃ
, ŠŗŠ°Š»Ń Ń
Š¾ŃŠ°Ńе закŃŃŃŃ."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ŠŃŃŃŠ¹Ńе Š½Š°Š»Š°Š“Š°Š¼Ń ŃŃŠæŠ»ŃŠ²Š°Š»ŃнŃŃ
апавŃŃŃŃŠ½Š½ŃŃ Ń Š»ŃŠ±Ń ŃŠ°Ń"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ŠŠ°Š± ŠŗŃŃŠ°Š²Š°ŃŃ ŃŃŠæŠ»ŃŠ²Š°Š»ŃŠ½ŃŠ¼Ń апавŃŃŃŃŠ½Š½ŃŠ¼Ń Š“Š»Ń ŠæŃŠ°Š³Ńам Ń ŃŠ°Š·Š¼Š¾Ń, наŃŃŃŠ½ŃŃŠµ ŃŃŃ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Š£ŃŠæŠ»ŃŠ²Š°Š»Ńнае апавŃŃŃŃŠ½Š½Šµ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ŠŃŃŠ°Š²Š°ŃŃ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Š£ŃŠæŠ»ŃŠ²Š°Š»Ńнае апавŃŃŃŃŠ½Š½Šµ аГŃ
ŃŠ»ŠµŠ½Š°."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ŠŠ°ŃŃŃŠ½ŃŃŠµ, каб ŠæŠµŃŠ°Š·Š°ŠæŃŃŃŃŃŃ Š³ŃŃŃ ŠæŃŠ°Š³ŃŠ°Š¼Ń Š“Š»Ń Š»ŠµŠæŃŠ°Š³Š° ŠæŃŠ°Š³Š»ŃŠ“Ń."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ŠŠ°ŃŃŃŠ½ŃŃŠµ, каб ŠæŠµŃŠ°Š·Š°ŠæŃŃŃŃŃŃ Š³ŃŃŃ ŠæŃŠ°Š³ŃŠ°Š¼Ń Š“Š»Ń Š·ŃŃŃŠ½ŠµŠ¹Ńага ŠæŃŠ°Š³Š»ŃŠ“Ń"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŠŠ¼ŃнŃŃŃ ŃŃŠ°Š“ноŃŃŠ½Ń Š±Š°ŠŗŠ¾Ń Š“Š»Ń Š³ŃŃŠ°Š¹ ŠæŃŠ°Š³ŃŠ°Š¼Ń Ń Š½Š°Š»Š°Š“Š°Ń
"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ŠŠ¼ŃнŃŃŃ ŃŃŠ°Š“ноŃŃŠ½Ń бакоŃ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ŠŃŠ°Š±Š»ŠµŠ¼Ń Š· ŠŗŠ°Š¼ŠµŃŠ°Š¹?\nŠŠ°ŃŃŃŠ½ŃŃŠµ, каб ŠæŠµŃŠ°Š°Š±ŃŃŠ°Š»ŃваŃŃ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ŠŠµ ŃŠ“алоŃŃ Š²ŃŠæŃавŃŃŃ?\nŠŠ°ŃŃŃŠ½ŃŃŠµ, каб аГнавŃŃŃ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ŠŃŃŠŗŃŃ
ŠæŃŠ°Š±Š»ŠµŠ¼ Š· ŠŗŠ°Š¼ŠµŃŠ°Š¹? ŠŠ°ŃŃŃŠ½ŃŃŠµ, каб аГŃ
ŃŠ»ŃŃŃ."</string> diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml index 0473f270239a..7aa98e5bc218 100644 --- a/libs/WindowManager/Shell/res/values-bg/strings.xml +++ b/libs/WindowManager/Shell/res/values-bg/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ŠŃемеŃŃŠ²Š°Š½Šµ Š³Š¾ŃŠµ вГŃŃŠ½Š¾"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ŠŃемеŃŃŠ²Š°Š½Šµ Š“Š¾Š»Ń Š²Š»ŃŠ²Š¾"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ŠŃемеŃŃŠ²Š°Š½Šµ Š“Š¾Š»Ń Š²Š“ŃŃŠ½Š¾"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ŃŠ°Š·Š³Ńване на <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ŃŠ²ŠøŠ²Š°Š½Šµ на <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ŠŠ°ŃŃŃŠ¾Š¹ŠŗŠø за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ŠŃŃ
вŃŃŠ»Ńне на Š±Š°Š»Š¾Š½ŃŠµŃŠ°Ńа"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ŠŠµŠ· Š±Š°Š»Š¾Š½ŃŠµŃа за ŃŠ°Š·Š³Š¾Š²Š¾Ńа"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Š Š°Š·Š±ŃŠ°Ń
"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ŠŃма ŃŠŗŠ¾ŃŠ¾ŃŠ½Šø Š±Š°Š»Š¾Š½ŃŠµŃа"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Š”ŠŗŠ¾ŃŠ¾ŃŠ½ŠøŃŠµ Šø оŃŃ
вŃŃŠ»ŠµŠ½ŠøŃе Š±Š°Š»Š¾Š½ŃеŃа ŃŠµ ŃŠµ ŠæŠ¾ŠŗŠ°Š·Š²Š°Ń ŃŃŠŗ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Š§Š°Ń Ń Š±Š°Š»Š¾Š½ŃŠµŃа"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ŠŠ¾Š²ŠøŃе ŃŠ°Š·Š³Š¾Š²Š¾ŃŠø ŃŠµ ŠæŠ¾ŠŗŠ°Š·Š²Š°Ń ŠŗŠ°ŃŠ¾ икони в Š“Š¾Š»Š½ŠøŃ ŃŠ³ŃŠ» на ŠµŠŗŃана ви. ŠŠ¾ŠŗŠ¾ŃŠ½ŠµŃŠµ, за Га ги ŃŠ°Š·Š³ŃŠ½ŠµŃŠµ, или ги ŠæŠ»ŃŠ·Š½ŠµŃе за оŃŃ
вŃŃŠ»Ńне."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Š£ŠæŃŠ°Š²Š»ŠµŠ½ŠøŠµ на Š±Š°Š»Š¾Š½ŃŠµŃŠ°Ńа по вŃŃŠŗŠ¾ Š²ŃŠµŠ¼Šµ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ŠŠ¾ŠŗŠ¾ŃŠ½ŠµŃŠµ ŃŃŠŗ, за Га ŃŠæŃавл. кои ŠæŃŠøŠ». Šø ŃŠ°Š·Š³Š¾Š²Š¾ŃŠø Š¼Š¾Š³Š°Ń Š“Š° ŠæŠ¾ŠŗŠ°Š·Š²Š°Ń Š±Š°Š»Š¾Š½ŃŠµŃа"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ŠŠ°Š»Š¾Š½Ńе"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Š£ŠæŃŠ°Š²Š»ŠµŠ½ŠøŠµ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ŠŠ°Š»Š¾Š½ŃŠµŃŠ¾ е оŃŃ
вŃŃŠ»ŠµŠ½Š¾."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ŠŠ¾ŠŗŠ¾ŃŠ½ŠµŃŠµ, за Га ŃŠµŃŃŠ°ŃŃŠøŃŠ°ŃŠµ ŃŠ¾Š²Š° ŠæŃŠøŠ»Š¾Š¶ŠµŠ½ŠøŠµ Ń ŃŠµŠ» по-ГобŃŃ ŠøŠ·Š³Š»ŠµŠ“."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ŠŠ¾ŠŗŠ¾ŃŠ½ŠµŃŠµ, за Га ŃŠµŃŃŠ°ŃŃŠøŃŠ°ŃŠµ ŃŠ¾Š²Š° ŠæŃŠøŠ»Š¾Š¶ŠµŠ½ŠøŠµ Ń ŃŠµŠ» по-ГобŃŃ ŠøŠ·Š³Š»ŠµŠ“"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŠŃŠ¾Š¼ŠµŠ½ŠµŃŠµ ŃŃŠ¾ŃŠ½Š¾ŃŠµŠ½ŠøŠµŃо на ŃŠ¾Š²Š° ŠæŃŠøŠ»Š¾Š¶ŠµŠ½ŠøŠµ в āŠŠ°ŃŃŃŠ¾Š¹ŠŗŠøā"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ŠŃŠ¾Š¼ŃŠ½Š° на ŃŃŠ¾ŃŠ½Š¾ŃŠµŠ½ŠøŠµŃо"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ŠŠ¼Š°Ńе ŠæŃоблеми Ń ŠŗŠ°Š¼ŠµŃŠ°Ńа?\nŠŠ¾ŠŗŠ¾ŃŠ½ŠµŃŠµ за ŃŠµŠ¼Š¾Š½ŃŠøŃŠ°Š½Šµ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ŠŃоблемŃŃ Š½Šµ ŃŠµ оŃŃŃŃŠ°Š½Šø?\nŠŠ¾ŠŗŠ¾ŃŠ½ŠµŃŠµ за вŃŃŃŠ°Š½Šµ в ŠæŃŠµŠ“ŠøŃŠ½Š¾Ńо ŃŃŃŃŠ¾Ńние"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ŠŃŠ¼Š°ŃŠµ ŠæŃŠ¾Š±Š»ŠµŠ¼Šø Ń ŠŗŠ°Š¼ŠµŃŠ°Ńа? ŠŠ¾ŠŗŠ¾ŃŠ½ŠµŃŠµ, за Га оŃŃ
вŃŃŠ»ŠøŃе."</string> diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml index 4fe1be0c455e..caad87e8f05f 100644 --- a/libs/WindowManager/Shell/res/values-bn/strings.xml +++ b/libs/WindowManager/Shell/res/values-bn/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ą¦ą¦Ŗą¦°ą§ ą¦”ą¦¾ą¦Øą¦¦ą¦æą¦ą§ সরান"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ą¦Øą¦æą¦ą§ ą¦¬ą¦¾ą¦ą¦¦ą¦æą¦ą§ সরান"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ą¦Øą¦æą¦ą§ ঔান ą¦¦ą¦æą¦ą§ সরান"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> বঔ় ą¦ą¦°ą§ą¦Ø"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¦ą¦”়াল ą¦ą¦°ą§ą¦Ø"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ą¦øą§ą¦ą¦æą¦ą¦ø"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল ą¦ą¦¾ą¦°ą¦æą¦ ą¦ą¦°ą§ą¦Ø"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą¦ą¦„ą§ą¦Ŗą¦ą¦„ন বাবল ą¦¹ą¦æą¦øą§ą¦¬ą§ ą¦¦ą§ą¦ą¦¾ą¦¬ą§ না"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą¦¬ą§ą¦ą§ą¦ą¦æ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ą¦ą§ą¦Øą¦ ą¦øą¦¾ą¦®ą§ą¦Ŗą§ą¦°ą¦¤ą¦æą¦ বাবল ą¦Øą§ą¦"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ą¦øą¦¾ą¦®ą§ą¦Ŗą§ą¦°ą¦¤ą¦æą¦ ঠবাতিল ą¦ą¦°ą¦¾ বাবল ą¦ą¦ą¦¾ą¦Øą§ ą¦¦ą§ą¦ą¦¾ যাবą§"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"বাবল ą¦¬ą§ą¦Æą¦¬ą¦¹ą¦¾ą¦° ą¦ą¦°ą§ ą¦ą§ą¦Æą¦¾ą¦ ą¦ą¦°ą§ą¦Ø"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ą¦Øą¦¤ą§ą¦Ø ą¦ą¦„ą§ą¦Ŗą¦ą¦„ন, ą¦ą¦Ŗą¦Øą¦¾ą¦° ą¦øą§ą¦ą§ą¦°ą¦æą¦Øą§ą¦° ą¦Øą¦æą¦ą§ ą¦ą§ą¦£ą¦¾ą¦° ą¦¦ą¦æą¦ą§ ą¦ą¦ą¦ą¦Ø ą¦¹ą¦æą¦øą§ą¦¬ą§ ą¦¦ą§ą¦ą¦¾ą¦Æą¦¼ą„¤ ą¦øą§ą¦ą¦æ বঔ় ą¦ą¦°ą¦¤ą§ ą¦ą§ą¦Æą¦¾ą¦Ŗ ą¦ą¦°ą§ą¦Ø বা বাতিল ą¦ą¦°ą¦¤ą§ ą¦ą§ą¦Øą§ ą¦ą¦Øą§ą¦Øą„¤"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ą¦Æą§ą¦ą§ą¦Øą¦ সময় বাবল ą¦Øą¦æą¦Æą¦¼ą¦Øą§ą¦¤ą§ą¦°ą¦£ ą¦ą¦°ą§ą¦Ø"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ą¦ą§ą¦Ø ą¦
ą§ą¦Æą¦¾ą¦Ŗ ą¦ ą¦ą¦„ą§ą¦Ŗą¦ą¦„ą¦Øą§ą¦° ą¦ą¦Øą§ą¦Æ ą¦¬ą¦¾ą¦¬ą¦²ą§ą¦° ą¦øą§ą¦¬ą¦æą¦§ą¦¾ ą¦ą¦¾ą¦Ø তা ą¦®ą§ą¦Æą¦¾ą¦Øą§ą¦ ą¦ą¦°ą¦¤ą§ ą¦ą¦ą¦¾ą¦Øą§ ą¦ą§ą¦Æą¦¾ą¦Ŗ ą¦ą¦°ą§ą¦Ø"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ą¦®ą§ą¦Æą¦¾ą¦Øą§ą¦ ą¦ą¦°ą§ą¦Ø"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল ą¦ą¦°ą¦¾ ą¦¹ą¦Æą¦¼ą§ą¦ą§ą„¤"</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą¦ą¦°ą¦ ą¦ą¦¾ą¦² ą¦ą¦æą¦ą¦Æą¦¼ą§ą¦° ą¦ą¦Øą§ą¦Æ ą¦ą¦ ą¦
ą§ą¦Æą¦¾ą¦Ŗ ą¦°ą¦æą¦øą§ą¦ą¦¾ą¦°ą§ą¦ ą¦ą¦°ą¦¤ą§ ą¦ą§ą¦Æą¦¾ą¦Ŗ ą¦ą¦°ą§ą¦Øą„¤"</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą¦ą¦°ą¦ ą¦ą¦¾ą¦² ą¦ą¦æą¦ą¦Æą¦¼ą§ą¦° ą¦ą¦Øą§ą¦Æ ą¦ą¦ ą¦
ą§ą¦Æą¦¾ą¦Ŗ ą¦°ą¦æą¦øą§ą¦ą¦¾ą¦°ą§ą¦ ą¦ą¦°ą¦¤ą§ ą¦ą§ą¦Æą¦¾ą¦Ŗ ą¦ą¦°ą§ą¦Ø"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą¦øą§ą¦ą¦æą¦ą¦ø ą¦„ą§ą¦ą§ ą¦ą¦ ą¦
ą§ą¦Æą¦¾ą¦Ŗą§ą¦° ą¦
ą§ą¦Æą¦¾ą¦øą§ą¦Ŗą§ą¦ą§ą¦ ą¦°ą§ą¦¶ą¦æą¦ ą¦Ŗą¦°ą¦æą¦¬ą¦°ą§ą¦¤ą¦Ø ą¦ą¦°ą§ą¦Ø"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą¦
ą§ą¦Æą¦¾ą¦øą§ą¦Ŗą§ą¦ą§ą¦ ą¦°ą§ą¦¶ą¦æą¦ ą¦Ŗą¦°ą¦æą¦¬ą¦°ą§ą¦¤ą¦Ø ą¦ą¦°ą§ą¦Ø"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ą¦ą§ą¦Æą¦¾ą¦®ą§ą¦°ą¦¾ ą¦øą¦ą¦ą§ą¦°ą¦¾ą¦Øą§ą¦¤ ą¦øą¦®ą¦øą§ą¦Æą¦¾?\ną¦°ą¦æą¦«ą¦æą¦ ą¦ą¦°ą¦¤ą§ ą¦ą§ą¦Æą¦¾ą¦Ŗ ą¦ą¦°ą§ą¦Ø"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ą¦ą¦ą¦Øą¦ সমাধান হয়নি?\ną¦°ą¦æą¦ą¦¾ą¦°ą§ą¦ ą¦ą¦°ą¦¾ą¦° ą¦ą¦Øą§ą¦Æ ą¦ą§ą¦Æą¦¾ą¦Ŗ ą¦ą¦°ą§ą¦Ø"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ą¦ą§ą¦Æą¦¾ą¦®ą§ą¦°ą¦¾ ą¦øą¦ą¦ą§ą¦°ą¦¾ą¦Øą§ą¦¤ ą¦øą¦®ą¦øą§ą¦Æą¦¾ ą¦Øą§ą¦? বাতিল ą¦ą¦°ą¦¤ą§ ą¦ą§ą¦Æą¦¾ą¦Ŗ ą¦ą¦°ą§ą¦Øą„¤"</string> diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml index b39b497f5c66..66e67b385cb8 100644 --- a/libs/WindowManager/Shell/res/values-bs/strings.xml +++ b/libs/WindowManager/Shell/res/values-bs/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pomjerite gore desno"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pomjeri dolje lijevo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pomjerite dolje desno"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proÅ”irivanje oblaÄiÄa <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sužavanje oblaÄiÄa <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke aplikacije <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblaÄiÄ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nemoj prikazivati razgovor u oblaÄiÄima"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Razumijem"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblaÄiÄa"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nedavni i odbaÄeni oblaÄiÄi Äe se pojaviti ovdje"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatajte koristeÄi oblaÄiÄe"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novi razgovori se pojavljuju kao ikone u donjem uglu ekrana. Dodirnite da ih proÅ”irite ili prevucite da ih odbacite."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Upravljajte oblaÄiÄima u svakom trenutku"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dodirnite ovdje da upravljate time koje aplikacije i razgovori mogu imati oblaÄiÄ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"OblaÄiÄ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"OblaÄiÄ je odbaÄen."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da ponovo pokrenete ovu aplikaciju radi boljeg prikaza."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da ponovo pokrenete ovu aplikaciju radi boljeg prikaza"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promijenite format slike aplikacije u Postavkama"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promijenite format slike"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s kamerom?\nDodirnite da ponovo namjestite"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index fe76e73135c9..62399ae07e7d 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mou a dalt a la dreta"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mou a baix a l\'esquerra"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mou a baix a la dreta"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"desplega <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"replega <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuració de l\'aplicació <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora la bombolla"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostris la conversa com a bombolla"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entesos"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hi ha bombolles recents"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bombolles recents i les ignorades es mostraran aquĆ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Xateja amb bombolles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Les converses noves es mostren com a icones en un extrem inferior de la pantalla. Toca per ampliar-les o arrossega per ignorar-les."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controla les bombolles en qualsevol moment"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toca aquĆ per gestionar quines aplicacions i converses poden fer servir bombolles"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Toca per reiniciar aquesta aplicació i obtenir una millor visualització."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Toca per reiniciar aquesta aplicació i obtenir una millor visualització"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Canvia la relació d\'aspecte d\'aquesta aplicació a Configuració"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Canvia la relació d\'aspecte"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tens problemes amb la cĆ mera?\nToca per resoldre\'ls"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la cĆ mera? Toca per ignorar."</string> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index 70e29705806f..8e0aba0fdec6 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"PÅesunout vpravo nahoru"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"PÅesunout vlevo dolÅÆ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"PÅesunout vpravo dolÅÆ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozbalit <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sbalit <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"NastavenĆ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ZavÅĆt bublinu"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovat konverzaci vĀ bublinĆ”ch"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ŽÔdnĆ© nedĆ”vnĆ© bubliny"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Zde se budou zobrazovat nedĆ”vnĆ© bubliny aĀ zavÅenĆ© bubliny"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatujte pomocĆ bublin"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"NovĆ© konverzace se zobrazĆ jako ikony vĀ dolnĆm rohu obrazovky. KlepnutĆm je rozbalĆte, pÅetaženĆm zavÅete."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"NastavenĆ bublin můžete kdykoli upravit"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"KlepnutĆm sem lze spravovat, kterĆ© aplikace aĀ konverzace mohou vytvĆ”Ået bubliny"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavÅena."</string> - <string name="restart_button_description" msgid="6712141648865547958">"KlepnutĆm tuto aplikaci kvÅÆli lepÅ”Ćmu zobrazenĆ restartujete."</string> + <string name="restart_button_description" msgid="4564728020654658478">"KlepnutĆm tuto aplikaci kvÅÆli lepÅ”Ćmu zobrazenĆ restartujete"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ZmÄnit vĀ NastavenĆ pomÄr stran tĆ©to aplikace"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ZmÄnit pomÄr stran"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ProblĆ©my sĀ fotoaparĆ”tem?\nKlepnutĆm vyÅeÅ”Ćte"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutĆm se vrĆ”tĆte"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ŽÔdnĆ© problĆ©my sĀ fotoaparĆ”tem? KlepnutĆm zavÅete."</string> diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml index c91cd7a956aa..d3989bcbd6f0 100644 --- a/libs/WindowManager/Shell/res/values-da/strings.xml +++ b/libs/WindowManager/Shell/res/values-da/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flyt op til hĆøjre"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flyt ned til venstre"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flyt ned til hĆøjre"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"udvid <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Indstillinger for <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Afvis boble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Vis ikke samtaler i bobler"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen seneste bobler"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nye bobler og afviste bobler vises her"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat ved hjƦlp af bobler"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nye samtaler vises som ikoner nederst pĆ„ din skƦrm. Tryk for at udvide dem, eller trƦk for at lukke dem."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Administrer bobler nĆ„r som helst"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tryk her for at administrere, hvilke apps og samtaler der kan vises i bobler"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Tryk for at genstarte denne app, sĆ„ visningen forbedres."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Tryk for at genstarte denne app, sĆ„ visningen forbedres"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Skift denne apps billedformat i Indstillinger"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Skift billedformat"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du problemer med dit kamera?\nTryk for at gendanne det oprindelige format"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"LĆøste det ikke problemet?\nTryk for at fortryde"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index 6ce475aa3c84..5d0ee2951e90 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -20,7 +20,7 @@ <string name="pip_phone_close" msgid="5783752637260411309">"SchlieĆen"</string> <string name="pip_phone_expand" msgid="2579292903468287504">"Maximieren"</string> <string name="pip_phone_settings" msgid="5468987116750491918">"Einstellungen"</string> - <string name="pip_phone_enter_split" msgid="7042877263880641911">"āGeteilter Bildschirmā aktivieren"</string> + <string name="pip_phone_enter_split" msgid="7042877263880641911">"Splitscreen aktivieren"</string> <string name="pip_menu_title" msgid="5393619322111827096">"Menü"</string> <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menü āBild im Bildā"</string> <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ist in Bild im Bild"</string> @@ -32,8 +32,8 @@ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"GrƶĆe anpassen"</string> <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"In Stash legen"</string> <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Aus Stash entfernen"</string> - <string name="dock_forced_resizable" msgid="7429086980048964687">"Die App funktioniert bei geteiltem Bildschirm unter UmstƤnden nicht"</string> - <string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"āGeteilter Bildschirmā wird in dieser App nicht unterstützt"</string> + <string name="dock_forced_resizable" msgid="7429086980048964687">"Die App funktioniert im Splitscreen-Modus unter UmstƤnden nicht"</string> + <string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Splitscreen wird in dieser App nicht unterstützt"</string> <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Diese App kann nur in einem einzigen Fenster geƶffnet werden."</string> <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundƤren Display mƶglicherweise nicht."</string> <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Die App unterstützt den Start auf sekundƤren Displays nicht."</string> @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Nach rechts oben verschieben"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Nach unten links verschieben"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Nach unten rechts verschieben"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> maximieren"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> minimieren"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Einstellungen für <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schlieĆen"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string> @@ -76,15 +78,21 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bubbles zum Chatten verwenden"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Neue Unterhaltungen erscheinen als Symbole unten auf dem Display. Du kannst sie durch Antippen maximieren und durch Ziehen schlieĆen."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bubble-Einstellungen festlegen"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tippe hier, um zu verwalten, welche Apps und Unterhaltungen als Bubble angezeigt werden kƶnnen"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Tippe, um diese App neu zu starten und die Ansicht zu verbessern."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Tippen, um diese App neu zu starten und die Ansicht zu verbessern"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"SeitenverhƤltnis der App in den Einstellungen Ƥndern"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"SeitenverhƤltnis Ƥndern"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Probleme mit der Kamera?\nZum Anpassen tippen."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum RückgƤngigmachen tippen."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum SchlieĆen tippen."</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Mehr sehen und erledigen"</string> - <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Weitere App hineinziehen, um den Bildschirm zu teilen"</string> + <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Für Splitscreen-Modus weitere App hineinziehen"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"AuĆerhalb einer App doppeltippen, um die Position zu Ƥndern"</string> <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ok"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Für weitere Informationen maximieren."</string> @@ -102,13 +110,12 @@ <string name="app_icon_text" msgid="2823268023931811747">"App-Symbol"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Vollbild"</string> <string name="desktop_text" msgid="1077633567027630454">"Desktopmodus"</string> - <string name="split_screen_text" msgid="1396336058129570886">"Geteilter Bildschirm"</string> + <string name="split_screen_text" msgid="1396336058129570886">"Splitscreen"</string> <string name="more_button_text" msgid="3655388105592893530">"Mehr"</string> <string name="float_button_text" msgid="9221657008391364581">"Frei schwebend"</string> <string name="select_text" msgid="5139083974039906583">"AuswƤhlen"</string> <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string> <string name="close_text" msgid="4986518933445178928">"SchlieĆen"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menü schlieĆen"</string> - <!-- no translation found for expand_menu_text (3847736164494181168) --> - <skip /> + <string name="expand_menu_text" msgid="3847736164494181168">"Menü ƶffnen"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml index ab5c44e9e07f..2b73528f7aef 100644 --- a/libs/WindowManager/Shell/res/values-el/strings.xml +++ b/libs/WindowManager/Shell/res/values-el/strings.xml @@ -66,20 +66,28 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ĪεĻακίνηĻĪ· εĻĪ¬Ī½Ļ Ī“ĪµĪ¾Ī¹Ī¬"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ĪεĻακίνηĻĪ· κάĻĻ Ī±ĻιĻĻεĻά"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ĪεĻακίνηĻĪ· κάĻĻ Ī“ĪµĪ¾Ī¹Ī¬"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ανάĻĻĻ
ξη <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ĻĻμĻĻĻ
ξη <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Ī”Ļ
θμίĻĪµĪ¹Ļ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ΠαĻάβλ. για ĻĻ
ννεĻ."</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Īα μην γίνει ĻĻοβολή ĻĪ·Ļ ĻĻ
ζήĻĪ·ĻĪ·Ļ Ļε ĻĻ
ννεĻάκια."</string> <string name="bubbles_user_education_title" msgid="2112319053732691899">"Ī£Ļ
ζηĻĪ®ĻĻε ĻĻĪ·ĻιμοĻοιĻνĻĪ±Ļ ĻĻ
ννεĻάκια."</string> <string name="bubbles_user_education_description" msgid="4215862563054175407">"Īι νĪĪµĻ ĻĻ
ζηĻĪ®ĻĪµĪ¹Ļ ĪµĪ¼ĻανίζονĻαι ĻĻ ĪŗĪ¹Ī½ĪæĻμενα εικονίΓια Ī® ĻĻ
ννεĻάκια. ΠαĻĪ®ĻĻε για να ανοίξεĻε ĻĪæ ĻĻ
ννεĻάκι. Ī£ĻĻεĻε για να ĻĪæ μεĻακινήĻεĻε."</string> <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ĪĪ»ĪγξĻε Ļα ĻĻ
ννεĻάκια ανά ĻάĻα ĻĻιγμή."</string> - <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ΠαĻĪ®ĻĻε ĪιαĻείĻιĻĪ· για να αĻενεĻγοĻοιήĻεĻε Ļα ĻĻ
ννεĻάκια αĻĻ Ī±Ļ
Ļήν Ļην εĻαĻμογή."</string> + <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ΠαĻĪ®ĻĻε ĪιαĻείĻιĻĪ· για να αĻενεĻγοĻοιήĻεĻε Ļα ĻĻ
ννεĻάκια αĻĻ Ī±Ļ
ĻĪ® Ļην εĻαĻμογή."</string> <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Το καĻάλαβα"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Īεν Ļ
ĻάĻĻĪæĻ
ν ĻĻĻĻĻαĻα ĻĻ
ννεĻάκια"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Τα ĻĻĻĻĻαĻα ĻĻ
ννεĻάκια και Ļα ĻĻ
ννεĻάκια ĻĪæĻ
ĻαĻαβλĪĻαĻε θα εμĻανίζονĻαι εΓĻ."</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Ī£Ļ
ζηĻĪ®ĻĻε ĻĻĪ·ĻιμοĻοιĻνĻĪ±Ļ ĻĻ
ννεĻάκια"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Īι νĪĪµĻ ĻĻ
ζηĻĪ®ĻĪµĪ¹Ļ ĪµĪ¼ĻανίζονĻαι ĻĻ ĪµĪ¹ĪŗĪæĪ½ĪÆĪ“Ī¹Ī± Ļε μια αĻĻ ĻĪ¹Ļ ĪŗĪ¬ĻĻ Ī³ĻĪ½ĪÆĪµĻ ĻĪ·Ļ ĪæĪøĻνηĻ. ΠαĻĪ®ĻĻε για να ĻĪ¹Ļ Ī±Ī½Ī±ĻĻĻξεĻε Ī® ĻĻĻεĻε για να ĻĪ¹Ļ ĻαĻαβλĪĻεĻε."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ĪĪ»ĪγξĻε Ļα ĻĻ
ννεĻάκια ανά ĻάĻα ĻĻιγμή."</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ΠαĻĪ®ĻĻε ĪµĪ“Ļ Ī³Ī¹Ī± ĻĪ· ΓιαĻείĻιĻĪ· εĻαĻμογĻν και ĻĻ
ζηĻĪ®ĻεĻν ĻĪæĻ
ĻĻοβάλλοĻ
ν ĻĻ
ννεĻάκια"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Ī£Ļ
ννεĻάκι"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ĪιαĻείĻιĻĪ·"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το ĻĻ
ννεĻάκι ĻαĻαβλĪĻθηκε."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ΠαĻĪ®ĻĻε για να εĻανεκκινήĻεĻε αĻ
ĻĪ® Ļην εĻαĻμογή για καλĻĻεĻĪ· ĻĻοβολή."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ΠαĻĪ®ĻĻε για να εĻανεκκινήĻεĻε αĻ
ĻĪ® Ļην εĻαĻμογή για καλĻĻεĻĪ· ĻĻοβολή"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ĪλλάξĻε Ļον Ī»Ļγο ΓιαĻĻάĻεĻν αĻ
ĻĪ®Ļ ĻĪ·Ļ ĪµĻαĻĪ¼ĪæĪ³Ī®Ļ ĻĻĪ¹Ļ Ī”Ļ
θμίĻειĻ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Īλλαγή Ī»ĻγοĻ
ΓιαĻĻάĻεĻν"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ī ĻοβλήμαĻα με Ļην κάμεĻα;\nΠαĻĪ®ĻĻε για εĻιΓιĻĻĪøĻĻĪ·."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Īεν ΓιοĻĪøĻθηκε;\nΠαĻĪ®ĻĻε για εĻαναĻĪæĻά."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Īεν ανĻιμεĻĻĻίζεĻε ĻĻοβλήμαĻα με Ļην κάμεĻα; ΠαĻĪ®ĻĻε για ĻαĻάβλεĻĪ·."</string> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index ea91014298df..2f0e898d5db7 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Donāt bubble conversation"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat using bubbles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Control bubbles at any time"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tap here to manage which apps and conversations can bubble"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didnāt fix it?\nTap to revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string> diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml index 01bdf95da918..a338905fa299 100644 --- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Donāt bubble conversation"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Got it"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat using bubbles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Control bubbles anytime"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tap here to manage which apps and conversations can bubble"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didnāt fix it?\nTap to revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index ea91014298df..2f0e898d5db7 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Donāt bubble conversation"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat using bubbles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Control bubbles at any time"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tap here to manage which apps and conversations can bubble"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didnāt fix it?\nTap to revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index ea91014298df..2f0e898d5db7 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Donāt bubble conversation"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat using bubbles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Control bubbles at any time"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tap here to manage which apps and conversations can bubble"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didnāt fix it?\nTap to revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string> diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml index f6dac79d0379..20344389ddcc 100644 --- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāMove top rightāāāāāā"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāMove bottom leftāāāāāā"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāMove bottom rightāāāāāā"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāexpand āāāāāāā<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>āāāāāāāāāāāāāā"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāācollapse āāāāāāā<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>āāāāāāāāāāāāāā"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>āāāāāāāā settingsāāāāāā"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāDismiss bubbleāāāāāā"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāDonāt bubble conversationāāāāāā"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāGot itāāāāāā"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāNo recent bubblesāāāāāā"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāRecent bubbles and dismissed bubbles will appear hereāāāāāā"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāChat using bubblesāāāāāā"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāNew conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them.āāāāāā"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāControl bubbles anytimeāāāāāā"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāTap here to manage which apps and conversations can bubbleāāāāāā"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāBubbleāāāāāā"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāManageāāāāāā"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāBubble dismissed.āāāāāā"</string> - <string name="restart_button_description" msgid="6712141648865547958">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāTap to restart this app for a better view.āāāāāā"</string> + <string name="restart_button_description" msgid="4564728020654658478">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāTap to restart this app for a better viewāāāāāā"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāChange this app\'s aspect ratio in Settingsāāāāāā"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāChange aspect ratioāāāāāā"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāCamera issues?āāāāāāā\nāāāāāāāāTap to refitāāāāāā"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāDidnāt fix it?āāāāāāā\nāāāāāāāāTap to revertāāāāāā"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāNo camera issues? Tap to dismiss.āāāāāā"</string> diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml index 0190ea85a30b..ebf67f6e164e 100644 --- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml +++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ubicar arriba a la derecha"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ubicar abajo a la izquierda"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expandir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Descartar burbuja"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbuja"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las que se descartaron aparecerĆ”n aquĆ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat con burbujas"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Las conversaciones nuevas aparecen como Ćconos en la esquina inferior de la pantalla. Presiona para expandirlas, o bien arrastra para descartarlas."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controla las burbujas"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Presiona para administrar las apps y conversaciones que pueden mostrar burbujas"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Presiona para reiniciar esta app y tener una mejor vista."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Presiona para reiniciar esta app y tener una mejor vista"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambiar la relación de aspecto de esta app en Configuración"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar relación de aspecto"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ĀæTienes problemas con la cĆ”mara?\nPresiona para reajustarla"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ĀæNo se resolvió?\nPresiona para revertir los cambios"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ĀæNo tienes problemas con la cĆ”mara? Presionar para descartar."</string> diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml index ea44bead6766..05171b2fc6dd 100644 --- a/libs/WindowManager/Shell/res/values-es/strings.xml +++ b/libs/WindowManager/Shell/res/values-es/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover arriba a la derecha"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover abajo a la izquierda."</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover abajo a la derecha"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"desplegar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Ajustes de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Cerrar burbuja"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar conversación en burbuja"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las cerradas aparecerĆ”n aquĆ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatea con burbujas"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Las nuevas conversaciones aparecen como iconos en una esquina inferior de la pantalla. Tócalas para ampliarlas o arrĆ”stralas para cerrarlas."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controla las burbujas cuando quieras"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toca aquĆ para gestionar quĆ© aplicaciones y conversaciones pueden usar burbujas"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Toca para reiniciar esta aplicación y obtener una mejor vista."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Toca para reiniciar esta aplicación y obtener una mejor vista"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambiar la relación de aspecto de esta aplicación en Ajustes"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar relación de aspecto"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ĀæProblemas con la cĆ”mara?\nToca para reajustar"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ĀæNo se ha solucionado?\nToca para revertir"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ĀæNo hay problemas con la cĆ”mara? Toca para cerrar."</string> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index 90feff32cc2b..3af3c16ede24 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Teisalda üles paremale"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Teisalda alla vasakule"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Teisalda alla paremale"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"laienda <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ahenda <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Rakenduse <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> seaded"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Sule mull"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ćra kuva vestlust mullina"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Selge"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hiljutisi mulle pole"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Siin kuvatakse hiljutised ja suletud mullid."</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Vestelge mullide abil"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Uued vestlused kuvatakse ekraanikuva alanurgas ikoonidena. Puudutage nende laiendamiseks vƵi lohistage neist loobumiseks."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Juhtige mulle igal ajal"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Puudutage siin, et hallata, milliseid rakendusi ja vestlusi saab mullina kuvada"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Puudutage, et see rakendus parema vaate jaoks taaskƤivitada."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Puudutage, et see rakendus parema vaate jaoks taaskƤivitada"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Muutke selle rakenduse kuvasuhet jaotises Seaded"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Muutke kuvasuhet"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kas teil on kaameraprobleeme?\nPuudutage ümberpaigutamiseks."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string> @@ -89,7 +97,7 @@ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Selge"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Laiendage lisateabe saamiseks."</string> <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Kas taaskƤivitada parema vaate saavutamiseks?"</string> - <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Saate rakenduse taaskƤivitada, et see nƤeks ekraanikuval parem vƤlja, kuid vƵite kaotada edenemise vƵi salvestamata muudatused"</string> + <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Saate rakenduse taaskƤivitada, et see nƤeks ekraanikuval parem vƤlja, kuid vƵite kaotada edenemise vƵi salvestamata muudatused."</string> <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Tühista"</string> <string name="letterbox_restart_restart" msgid="8529976234412442973">"TaaskƤivita"</string> <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ćra kuva uuesti"</string> diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml index de27a8045e85..1dbc57b73f6c 100644 --- a/libs/WindowManager/Shell/res/values-eu/strings.xml +++ b/libs/WindowManager/Shell/res/values-eu/strings.xml @@ -36,7 +36,7 @@ <string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"Aplikazioak ez du onartzen pantaila zatitua"</string> <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Leiho bakar batean ireki daiteke aplikazioa."</string> <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Baliteke aplikazioak ez funtzionatzea bigarren mailako pantailetan."</string> - <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikazioa ezin da abiarazi bigarren mailako pantailatan."</string> + <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikazioa ezin da exekutatu bigarren mailako pantailatan."</string> <string name="accessibility_divider" msgid="6407584574218956849">"Pantaila-zatitzailea"</string> <string name="divider_title" msgid="1963391955593749442">"Pantaila-zatitzailea"</string> <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ezarri ezkerraldea pantaila osoan"</string> @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Eraman goialdera, eskuinetara"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Eraman behealdera, ezkerretara"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Eraman behealdera, eskuinetara"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"zabaldu <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tolestu <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aplikazioaren ezarpenak"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Baztertu burbuila"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ez erakutsi elkarrizketak burbuila gisa"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ados"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ez dago azkenaldiko burbuilarik"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Azken burbuilak eta baztertutakoak agertuko dira hemen"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Txateatu burbuilak erabilita"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Elkarrizketa berriak ikono gisa agertzen dira pantailaren beheko izkinan. Zabaltzeko, saka itzazu. Baztertzeko, aldiz, arrasta itzazu."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrolatu burbuilak edonoiz"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Sakatu hau burbuiletan zein aplikazio eta elkarrizketa ager daitezkeen kudeatzeko"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Hobeto ikusteko, sakatu hau aplikazioa berrabiarazteko."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Hobeto ikusteko, sakatu hau, eta aplikazioa berrabiarazi egingo da"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Aldatu aplikazioaren aspektu-erlazioa ezarpenetan"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Aldatu aspektu-erlazioa"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Arazoak dauzkazu kamerarekin?\nBerriro doitzeko, sakatu hau."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string> @@ -102,7 +110,7 @@ <string name="app_icon_text" msgid="2823268023931811747">"Aplikazioaren ikonoa"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Pantaila osoa"</string> <string name="desktop_text" msgid="1077633567027630454">"Ordenagailuetarako modua"</string> - <string name="split_screen_text" msgid="1396336058129570886">"Pantaila zatitua"</string> + <string name="split_screen_text" msgid="1396336058129570886">"Pantaila zatitzea"</string> <string name="more_button_text" msgid="3655388105592893530">"Gehiago"</string> <string name="float_button_text" msgid="9221657008391364581">"Leiho gainerakorra"</string> <string name="select_text" msgid="5139083974039906583">"Hautatu"</string> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index 13a2ea2db140..f7547602dcea 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -59,13 +59,15 @@ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Ų®Ų±ŁŲ¬ Ų§Ų² Ā«ŲŲ§ŁŲŖ ŪŚ©āŲÆŲ³ŲŖŪĀ»"</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"ŲŖŁŲøŪŁ
Ų§ŲŖ ŲØŲ±Ų§Ū ŲŲØŲ§ŲØŚ©āŁŲ§Ū <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Ų³Ų±Ų±ŪŲ²"</string> - <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Ų§ŁŲ²ŁŲÆŁ برگؓت ب٠پؓتŁ"</string> + <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Ų§ŁŲ²ŁŲÆŁ برگؓت٠ب٠پؓتŁ"</string> <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> Ų§Ų² <xliff:g id="APP_NAME">%2$s</xliff:g>"</string> <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> Ų§Ų² <xliff:g id="APP_NAME">%2$s</xliff:g> Ł <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> Ł
ŁŲ±ŲÆ ŲØŪŲ“ŲŖŲ±"</string> <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Ų§ŁŲŖŁŲ§Ł ŲØŁ ŲØŲ§ŁŲ§ Ų³Ł
ŲŖ Ų±Ų§Ų³ŲŖ"</string> <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ų§ŁŲŖŁŲ§Ł ŲØŁ ŲØŲ§ŁŲ§ Ų³Ł
ŲŖ ŚŁ¾"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ų§ŁŲŖŁŲ§Ł ب٠پاŪŪŁ Ų³Ł
ŲŖ Ų±Ų§Ų³ŲŖ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ų§ŁŲŖŁŲ§Ł ب٠پاŪŪŁ Ų³Ł
ŲŖ ŚŁ¾"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Ų§Ų²ŁŁ
ŲØŲ§Ų² کرد٠<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Ų¬Ł
Ų¹ کرد٠<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ŲŖŁŲøŪŁ
Ų§ŲŖ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ų±ŲÆ کرد٠ŲŲØŲ§ŲØŚ©"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ł
Ś©Ų§ŁŁ
Ł ŲÆŲ± ŲŲØŲ§ŲØ ŁŲ“Ų§Ł ŲÆŲ§ŲÆŁ ŁŲ“ŁŲÆ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ł
ŲŖŁŲ¬ŁāŲ§Ł
"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ŁŪŚ ŲŲØŲ§ŲØŚ© Ų¬ŲÆŪŲÆŪ ŁŲ¬ŁŲÆ ŁŲÆŲ§Ų±ŲÆ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ŲŲØŲ§ŲØŚ©āŁŲ§Ū Ų§Ų®ŪŲ± Ł ŲŲØŲ§ŲØŚ©āŁŲ§Ū Ų±ŲÆŲ“ŲÆŁ Ų§ŪŁŲ¬Ų§ ŲøŲ§ŁŲ± Ų®ŁŲ§ŁŁŲÆ Ų“ŲÆ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"گپ Ų²ŲÆŁ ŲØŲ§Ų§Ų³ŲŖŁŲ§ŲÆŁ Ų§Ų² ŲŲØŲ§ŲØŚ©"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Ł
Ś©Ų§ŁŁ
ŁāŁŲ§Ū Ų¬ŲÆŪŲÆ ŲØŁāŲµŁŲ±ŲŖ ŁŁ
Ų§ŲÆ ŲÆŲ± ŚÆŁŲ“٠پاŪŪŁ ŲµŁŲŁāŁŁ
Ų§ŪŲ“ ŁŲ“Ų§Ł ŲÆŲ§ŲÆŁ Ł
ŪāŲ“ŁŲÆ. ŲØŲ±Ų§Ū Ų§Ų²ŁŁ
بازکرد٠آŁāŁŲ§ Ų¶Ų±ŲØŁ ŲØŲ²ŁŪŲÆ ŪŲ§ ŲØŲ±Ų§Ū ŲØŲ³ŲŖŁŲ Ų¢ŁāŁŲ§ Ų±Ų§ بکؓŪŲÆ."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Ś©ŁŲŖŲ±Ł ŲŲØŲ§ŲØŚ©āŁŲ§ ŲÆŲ± ŁŲ±Ų²Ł
Ų§ŁŪ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ŲØŲ±Ų§Ū Ł
ŲÆŪŲ±ŪŲŖ Ų§ŪŁŚ©Ł Ś©ŲÆŲ§Ł
ŲØŲ±ŁŲ§Ł
ŁāŁŲ§ Ł Ł
Ś©Ų§ŁŁ
ŁāŁŲ§ ŲŲØŲ§ŲØ ŲÆŲ§Ų“ŲŖŁ ŲØŲ§Ų“ŁŲÆŲ Ų¶Ų±ŲØŁ ŲØŲ²ŁŪŲÆ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ŲŲØŲ§ŲØ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Ł
ŲÆŪŲ±ŪŲŖ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ŲŲØŲ§ŲØŚ© Ų±ŲÆ Ų“ŲÆ."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ŲØŲ±Ų§Ū ŲÆŲ§Ų“ŲŖŁ ŁŁ
Ų§ŪŪ ŲØŁŲŖŲ±Ų Ų¶Ų±ŲØŁ ŲØŲ²ŁŪŲÆ ŲŖŲ§ Ų§ŪŁ ŲØŲ±ŁŲ§Ł
Ł ŲØŲ§Ų²Ų±Ų§ŁāŲ§ŁŲÆŲ§Ų²Ū Ų“ŁŲÆ."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ŲØŲ±Ų§Ū ŲÆŲ§Ų“ŲŖŁ ŁŁ
Ų§ŪŪ ŲØŁŲŖŲ±Ų Ų¶Ų±ŲØŁ ŲØŲ²ŁŪŲÆ ŲŖŲ§ Ų§ŪŁ ŲØŲ±ŁŲ§Ł
Ł ŲØŲ§Ų²Ų±Ų§ŁāŲ§ŁŲÆŲ§Ų²Ū Ų“ŁŲÆ"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŁŲ³ŲØŲŖ Ų§ŲØŲ¹Ų§ŲÆŪ Ų§ŪŁ ŲØŲ±ŁŲ§Ł
Ł Ų±Ų§ ŲÆŲ± «تŁŲøŪŁ
Ų§ŲŖĀ» ŲŖŲŗŪŪŲ± ŲÆŁŪŲÆ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ŲŖŲŗŪŪŲ± ŁŲ³ŲØŲŖ Ų§ŲØŲ¹Ų§ŲÆŪ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ŲÆŁŲ±ŲØŪŁ Ł
ؓک٠داردŲ\nŲØŲ±Ų§Ū ŲŖŁŲøŪŁ
Ł
Ų¬ŲÆŲÆ Ų§ŁŲÆŲ§Ų²Ł Ų¶Ų±ŲØŁ ŲØŲ²ŁŪŲÆ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ł
ؓک٠برطر٠ŁŲ“ŲÆŲ\nŲØŲ±Ų§Ū ŲØŲ±ŚÆŲ±ŲÆŲ§ŁŲÆŁ Ų¶Ų±ŲØŁ ŲØŲ²ŁŪŲÆ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ŲÆŁŲ±ŲØŪŁ Ł
Ų“Ś©ŁŪ ŁŲÆŲ§Ų±ŲÆŲ ŲØŲ±Ų§Ū ŲØŲ³ŲŖŁ Ų¶Ų±ŲØŁ ŲØŲ²ŁŪŲÆ."</string> diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml index 92fa76013134..9094c7335571 100644 --- a/libs/WindowManager/Shell/res/values-fi/strings.xml +++ b/libs/WindowManager/Shell/res/values-fi/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"SiirrƤ oikeaan ylƤreunaan"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"SiirrƤ vasempaan alareunaan"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"SiirrƤ oikeaan alareunaan"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"laajenna <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tiivistƤ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: asetukset"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ohita kupla"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ĆlƤ nƤytƤ kuplia keskusteluista"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Okei"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ei viimeaikaisia kuplia"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viimeaikaiset ja ƤskettƤin ohitetut kuplat nƤkyvƤt tƤƤllƤ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chattaile kuplien avulla"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Uudet keskustelut nƤkyvƤt kuvakkeina nƤytƶn alareunassa. Laajenna ne napauttamalla tai hylkƤƤ ne vetƤmƤllƤ."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Muuta kuplien asetuksia milloin tahansa"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Valitse napauttamalla tƤstƤ, mitkƤ sovellukset ja keskustelut voivat kuplia"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"YllƤpidƤ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Napauta, niin sovellus kƤynnistyy uudelleen paremmin nƤytƶlle sopivana."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Napauta, niin sovellus kƤynnistyy uudelleen paremmin nƤytƶlle sopivana"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Muuta tƤmƤn sovelluksen kuvasuhdetta Asetuksissa"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Vaihda kuvasuhdetta"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Onko kameran kanssa ongelmia?\nKorjaa napauttamalla"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikƶ ongelma ratkennut?\nKumoa napauttamalla"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? HylkƤƤ napauttamalla."</string> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index 7814b7d38fed..b26c1b4e3018 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"DĆ©placer dans coin sup. droit"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"DĆ©placer dans coin inf. gauche"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"DĆ©placer dans coin inf. droit"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"dĆ©velopper <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"rĆ©duire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ParamĆØtres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorer la bulle"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher les conversations dans des bulles"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle rĆ©cente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles rĆ©centes et les bulles ignorĆ©es s\'afficheront ici"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Clavarder en utilisant des bulles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Les nouvelles conversations apparaissent sous forme d\'icĆ“nes dans le coin infĆ©rieur de votre Ć©cran. Touchez-les icĆ“nes pour dĆ©velopper les conversations ou faites-les glisser pour les supprimer."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"GĆ©rez les bulles en tout temps"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Touchez ici pour gĆ©rer les applis et les conversations Ć inclure aux bulles"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"GĆ©rer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorĆ©e."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Touchez pour redĆ©marrer cette application afin d\'obtenir un meilleur affichage."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Touchez pour redĆ©marrer cette application afin d\'obtenir un meilleur affichage"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Changer les proportions de cette application dans les paramĆØtres"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Modifier les proportions"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ProblĆØmes d\'appareil photo?\nTouchez pour rĆ©ajuster"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ProblĆØme non rĆ©solu?\nTouchez pour rĆ©tablir"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problĆØme d\'appareil photo? Touchez pour ignorer."</string> diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml index da5b5c9bfeba..6e6420a22f50 100644 --- a/libs/WindowManager/Shell/res/values-fr/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"DĆ©placer en haut Ć droite"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"DĆ©placer en bas Ć gauche"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"DĆ©placer en bas Ć droite"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"DĆ©velopper <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"RĆ©duire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ParamĆØtres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Fermer la bulle"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher la conversation dans une bulle"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle rĆ©cente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles rĆ©centes et ignorĆ©es s\'afficheront ici"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatter via des bulles"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Les nouvelles conversations apparaissent sous forme d\'icĆ“nes dans l\'un des coins infĆ©rieurs de votre Ć©cran. Appuyez dessus pour les dĆ©velopper ou faites-les glisser pour les supprimer."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ContrĆ“lez les bulles Ć tout moment"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Appuyez ici pour gĆ©rer les applis et conversations s\'affichant dans des bulles"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"GĆ©rer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermĆ©e."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Appuyez pour redĆ©marrer cette appli et avoir une meilleure vue."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Appuyez pour redĆ©marrer cette appli et obtenir une meilleure vue."</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Modifiez le format de cette appli dans les ParamĆØtres."</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Modifier le format"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ProblĆØmes d\'appareil photoĀ ?\nAppuyez pour rĆ©ajuster"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ProblĆØme non rĆ©soluĀ ?\nAppuyez pour rĆ©tablir"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problĆØme d\'appareil photoĀ ? Appuyez pour ignorer."</string> @@ -89,7 +97,7 @@ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"DĆ©veloppez pour obtenir plus d\'informations"</string> <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"RedĆ©marrer pour amĆ©liorer l\'affichageĀ ?"</string> - <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Vous pouvez redĆ©marrer l\'appli pour en amĆ©liorer son aspect sur votre Ć©cran, mais vous risquez de perdre votre progression ou les modifications non enregistrĆ©es"</string> + <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Vous pouvez redĆ©marrer l\'appli pour un meilleur rendu sur votre Ć©cran, mais il se peut que vous perdiez votre progression ou les modifications non enregistrĆ©es"</string> <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Annuler"</string> <string name="letterbox_restart_restart" msgid="8529976234412442973">"RedĆ©marrer"</string> <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne plus afficher"</string> diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml index c08cff86f63c..bf3a45b46484 100644 --- a/libs/WindowManager/Shell/res/values-gl/strings.xml +++ b/libs/WindowManager/Shell/res/values-gl/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover Ć” parte superior dereita"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover Ć” parte infer. esquerda"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover Ć” parte inferior dereita"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"despregar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar burbulla"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mostrar a conversa como burbulla"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Non hai burbullas recentes"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"As burbullas recentes e ignoradas aparecerĆ”n aquĆ."</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatea usando burbullas"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"As conversas novas móstranse como iconas nunha das esquinas inferiores da pantalla. Tócaas para amplialas ou arrĆ”straas para pechalas."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controlar as burbullas"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toca para xestionar as aplicacións e conversas que poden aparecer en burbullas"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Toca o botón para reiniciar esta aplicación e gozar dunha mellor visualización."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Toca o botón para reiniciar esta aplicación e gozar dunha mellor visualización"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambia a proporción desta aplicación en Configuración"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar a proporción"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tes problemas coa cĆ”mara?\nToca para reaxustala"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cĆ”mara? Tocar para ignorar."</string> diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml index 2a521991ef8d..84c818230621 100644 --- a/libs/WindowManager/Shell/res/values-gu/strings.xml +++ b/libs/WindowManager/Shell/res/values-gu/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ąŖąŖŖąŖ° ąŖąŖ®ąŖ£ą« ąŖąŖøą«ąŖ”ą«"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ąŖØą«ąŖą« ąŖ”ąŖ¾ąŖ¬ą« ąŖąŖøą«ąŖ”ą«"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ąŖØą«ąŖą« ąŖąŖ®ąŖ£ą« ąŖąŖøą«ąŖ”ą«"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ąŖ®ą«ąŖą«ąŖ ąŖąŖ°ą«"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ąŖØąŖ¾ąŖØą«ąŖ ąŖąŖ°ą«"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ąŖøą«ąŖąŖæąŖąŖ"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ąŖ¬ąŖ¬ąŖ²ąŖØą« ąŖą«ąŖ”ą« ąŖ¦ą«"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ąŖµąŖ¾ąŖ¤ąŖą«ąŖ¤ąŖØą« બબલ ąŖąŖ°ąŖ¶ą« ąŖØąŖ¹ą«ąŖ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ąŖøąŖ®ąŖąŖ¾ąŖ ąŖąŖÆą«ąŖ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ąŖ¤ąŖ¾ąŖą«ąŖ¤ąŖ°ąŖØąŖ¾ ąŖą«ąŖ બબલ ąŖØąŖ„ą«"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ąŖąŖąŖ¦ąŖ® નવા બબલ ąŖ
ąŖØą« ąŖą«ąŖ”ą« ąŖ¦ą«ąŖ§ą«ąŖ²ąŖ¾ બબલ ąŖ
ąŖ¹ą«ąŖ ąŖ¦ą«ąŖąŖ¾ąŖ¶ą«"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ąŖ¬ąŖ¬ąŖ²ąŖØą« ąŖąŖŖąŖÆą«ąŖ ąŖąŖ°ą«ąŖØą« ąŖą«
ąŖ ąŖąŖ°ą«"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ąŖØąŖµą« ąŖµąŖ¾ąŖ¤ąŖą«ąŖ¤ą« ąŖ¤ąŖ®ąŖ¾ąŖ°ą« ąŖøą«ąŖą«ąŖ°ą«ąŖØąŖØąŖ¾ ąŖØą«ąŖą«ąŖØąŖ¾ ąŖą«ąŖ£ąŖ¾ąŖ®ąŖ¾ąŖ ąŖąŖąŖąŖØ ąŖ¤ąŖ°ą«ąŖą« ąŖ¦ą«ąŖąŖ¾ąŖÆ ąŖą«. ąŖ¤ą«ąŖ®ąŖØą« ąŖ®ą«ąŖą« ąŖąŖ°ąŖµąŖ¾ ąŖ®ąŖ¾ąŖą«, ąŖ¤ą«ąŖ®ąŖØąŖ¾ ąŖŖąŖ° ąŖą«
ąŖŖ ąŖąŖ°ą« ąŖ
઄વા ąŖ¤ą«ąŖ®ąŖØą« ąŖą«ąŖ”ą« ąŖ¦ą«ąŖµąŖ¾ ąŖ®ąŖ¾ąŖą«, ąŖ¤ą«ąŖ®ąŖØą« ąŖą«ąŖąŖą«."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ąŖ¬ąŖ¬ąŖ²ąŖØą« ąŖą«ąŖąŖŖąŖ£ ąŖøąŖ®ąŖÆą« ąŖØąŖæąŖÆąŖąŖ¤ą«ąŖ°ąŖæąŖ¤ ąŖąŖ°ą« ąŖą«"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ąŖąŖ ąŖąŖŖ ąŖ
ąŖØą« ąŖµąŖ¾ąŖ¤ąŖą«ąŖ¤ą«ąŖØą« બબલ ąŖąŖ°ąŖµąŖ¾ ąŖ®ąŖ¾ąŖą« ąŖą« ąŖ¤ą« ąŖ®ą«ąŖØą«ąŖ ąŖąŖ°ąŖµąŖ¾ ąŖ®ąŖ¾ąŖą«, ąŖ
ąŖ¹ą«ąŖ ąŖą«
ąŖŖ ąŖąŖ°ą«"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ąŖ®ą«ąŖØą«ąŖ ąŖąŖ°ą«"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ ąŖą«ąŖ”ą« ąŖ¦ą«ąŖµąŖ¾ąŖÆą«."</string> - <string name="restart_button_description" msgid="6712141648865547958">"વધૠસારા ąŖµą«ąŖÆą« ąŖ®ąŖ¾ąŖą«, ąŖ ąŖąŖŖąŖØą« ąŖ«ąŖ°ą« ąŖ¶ąŖ°ą« ąŖąŖ°ąŖµąŖ¾ ąŖą«
ąŖŖ ąŖąŖ°ą«."</string> + <string name="restart_button_description" msgid="4564728020654658478">"વધૠસારા ąŖµą«ąŖÆą« ąŖ®ąŖ¾ąŖą«, ąŖ ąŖąŖŖąŖØą« ąŖ«ąŖ°ą« ąŖ¶ąŖ°ą« ąŖąŖ°ąŖµąŖ¾ ąŖą«
ąŖŖ ąŖąŖ°ą«"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ąŖøą«ąŖąŖæąŖąŖąŖ®ąŖ¾ąŖ ąŖ ąŖąŖŖąŖØą« ąŖøąŖ¾ąŖŖą«ąŖą«ąŖ· ąŖą«ąŖ£ą«ąŖ¤ą«ąŖ¤ąŖ° બદલą«"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ąŖøąŖ¾ąŖŖą«ąŖą«ąŖ· ąŖą«ąŖ£ą«ąŖ¤ą«ąŖ¤ąŖ° બદલą«"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ąŖą«
ąŖ®ą«ąŖ°ąŖ¾ąŖ®ąŖ¾ąŖ ąŖøąŖ®ąŖøą«ąŖÆąŖ¾ąŖ ąŖą«?\nąŖ«ąŖ°ą«ąŖ„ą« ąŖ«ąŖæąŖ ąŖąŖ°ąŖµąŖ¾ ąŖ®ąŖ¾ąŖą« ąŖą«
ąŖŖ ąŖąŖ°ą«"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ąŖøą«ąŖ§ąŖ¾ąŖ°ą« ąŖØąŖ„ą« ąŖ„ąŖÆą«?\nąŖŖąŖ¹ą«ąŖ²ąŖ¾ąŖąŖØąŖ¾ ąŖŖąŖ° ąŖŖąŖ¾ąŖą«ąŖ ąŖ«ą«ąŖ°ąŖµąŖµąŖ¾ ąŖ®ąŖ¾ąŖą« ąŖą«
ąŖŖ ąŖąŖ°ą«"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ąŖą«
ąŖ®ą«ąŖ°ąŖ¾ąŖ®ąŖ¾ąŖ ąŖą«ąŖ ąŖøąŖ®ąŖøą«ąŖÆąŖ¾ ąŖØąŖ„ą«? ąŖą«ąŖ”ą« ąŖ¦ą«ąŖµąŖ¾ ąŖ®ąŖ¾ąŖą« ąŖą«
ąŖŖ ąŖąŖ°ą«."</string> diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index fb5040b36a89..8068f50d9cf6 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ą¤øą¤¬ą¤øą„ ą¤ą¤Ŗą¤° ą¤¦ą¤¾ą¤ą¤ ą¤ą¤° ą¤²ą„ ą¤ą¤¾ą¤ą¤"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ą¤¬ą¤¾ą¤ą¤ ą¤ą¤° ą¤øą¤¬ą¤øą„ ą¤Øą„ą¤ą„ ą¤²ą„ ą¤ą¤¾ą¤ą¤"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ą¤øą¤¬ą¤øą„ ą¤Øą„ą¤ą„ ą¤¦ą¤¾ą¤ą¤ ą¤ą¤° ą¤²ą„ ą¤ą¤¾ą¤ą¤"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¤ą„ बऔ़ा ą¤ą¤°ą„ą¤"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¤ą„ ą¤ą„ą¤ą¤¾ ą¤ą¤°ą„ą¤"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ą¤ą„ ą¤øą„ą¤ą¤æą¤ą¤"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल ą¤ą¤¾ą¤°ą¤æą¤ ą¤ą¤°ą„ą¤"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą¤¬ą¤¾ą¤¤ą¤ą„त ą¤ą„ बबल न ą¤ą¤°ą„ą¤"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą¤ ą„ą¤ हą„"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ą¤¹ą„ ą¤ą„ ą¤ą„ą¤ ą¤¬ą¤¬ą¤²ą„ą¤ø ą¤Øą¤¹ą„ą¤ ą¤¹ą„ą¤"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ą¤¹ą„ ą¤ą„ ą¤¬ą¤¬ą¤²ą„ą¤ø ą¤ą¤° ą¤¹ą¤ą¤¾ą¤ ą¤ą¤ ą¤¬ą¤¬ą¤²ą„ą¤ø ą¤Æą¤¹ą¤¾ą¤ ą¤¦ą¤æą¤ą„ą¤ą¤ą„"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ą¤¬ą¤¬ą¤²ą„ą¤ø ą¤ą¤¾ ą¤ą¤øą„ą¤¤ą„ą¤®ą¤¾ą¤² ą¤ą¤°ą¤ą„ ą¤ą„ą¤ ą¤ą¤°ą„ą¤"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ą¤Øą¤ ą¤¬ą¤¾ą¤¤ą¤ą„त, ą¤ą¤Ŗą¤ą„ ą¤øą„ą¤ą„ą¤°ą„ą¤Ø पर ą¤øą¤¬ą¤øą„ ą¤Øą„ą¤ą„ ą¤ą¤ą¤ą„न ą¤ą„ ą¤¤ą„ą¤° पर ą¤¦ą¤æą¤ą¤¤ą„ ą¤¹ą„ą¤. ą¤ą¤æą¤øą„ ą¤ą¤ą¤ą„न ą¤ą„ बऔ़ा ą¤ą¤°ą¤Øą„ ą¤ą„ ą¤²ą¤æą¤ ą¤ą¤ø पर ą¤ą„प ą¤ą¤°ą„ठया ą¤ą¤¾ą¤°ą¤æą¤ ą¤ą¤°ą¤Øą„ ą¤ą„ ą¤²ą¤æą¤ ą¤ą¤øą„ ą¤ą„ą¤ą¤ą„ą¤ ą¤ą¤° ą¤ą„ą¤”ą¤¼ą„ą¤."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ą¤ą¤¬ ą¤ą¤¾ą¤¹ą„ą¤, ą¤¬ą¤¬ą¤²ą„ą¤ø ą¤ą„ ą¤øą„ą¤µą¤æą¤§ą¤¾ ą¤ą„ ą¤ą¤ą¤ą„ą¤°ą„ą¤² ą¤ą¤°ą„ą¤"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ą¤ą¤æą¤øą„ ą¤ą¤Ŗą„ą¤²ą¤æą¤ą„शन ą¤ą¤° ą¤¬ą¤¾ą¤¤ą¤ą„त ą¤ą„ लिठबबल ą¤ą„ ą¤øą„ą¤µą¤æą¤§ą¤¾ ą¤ą„ ą¤®ą„ą¤Øą„ą¤ ą¤ą¤°ą¤Øą„ ą¤ą„ ą¤²ą¤æą¤ ą¤Æą¤¹ą¤¾ą¤ ą¤ą„प ą¤ą¤°ą„ą¤"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ą¤®ą„ą¤Øą„ą¤ ą¤ą¤°ą„ą¤"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल ą¤ą¤¾ą¤°ą¤æą¤ ą¤ą¤æą¤Æą¤¾ ą¤ą¤Æą¤¾."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą¤ą„प ą¤ą¤°ą¤ą„ ą¤ą¤Ŗą„ą¤²ą¤æą¤ą„शन ą¤ą„ ą¤°ą„ą¤øą„ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą„ą¤ ą¤ą¤° ą¤¬ą„ą¤¹ą¤¤ą¤° ą¤µą„ą¤Æą„ ą¤Ŗą¤¾ą¤ą¤."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą¤¬ą„ą¤¹ą¤¤ą¤° ą¤µą„ą¤Æą„ ą¤Ŗą¤¾ą¤Øą„ ą¤ą„ लिą¤, ą¤ą„प ą¤ą¤°ą¤ą„ ą¤ą¤Ŗą„ą¤²ą¤æą¤ą„शन ą¤ą„ ą¤°ą„ą¤øą„ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą„ą¤"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą¤øą„ą¤ą¤æą¤ą¤ ą¤®ą„ą¤ ą¤ą¤¾ą¤ą¤° ą¤ą¤ø ą¤ą¤Ŗą„ą¤²ą¤æą¤ą„शन ą¤ą¤¾ ą¤ą¤øą¤Ŗą„ą¤ą„ą¤ ą¤°ą„ą¤¶ą¤æą¤Æą„ (ą¤²ą¤ą¤¬ą¤¾ą¤-ą¤ą„ą¤”ą¤¼ą¤¾ą¤ ą¤ą¤¾ ą¤
ą¤Øą„ą¤Ŗą¤¾ą¤¤) ą¤¬ą¤¦ą¤²ą„ą¤"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą¤ą¤øą¤Ŗą„ą¤ą„ą¤ ą¤°ą„ą¤¶ą¤æą¤Æą„ (ą¤²ą¤ą¤¬ą¤¾ą¤-ą¤ą„ą¤”ą¤¼ą¤¾ą¤ ą¤ą¤¾ ą¤
ą¤Øą„ą¤Ŗą¤¾ą¤¤) ą¤¬ą¤¦ą¤²ą„ą¤"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ą¤ą„या ą¤ą„ą¤®ą¤°ą„ ą¤øą„ ą¤ą„ą¤”ą¤¼ą„ ą¤ą„ą¤ ą¤øą¤®ą¤øą„ą¤Æą¤¾ हą„?\nफिर ą¤øą„ ą¤«ą¤¼ą¤æą¤ ą¤ą¤°ą¤Øą„ ą¤ą„ ą¤²ą¤æą¤ ą¤ą„प ą¤ą¤°ą„ą¤"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ą¤ą„या ą¤øą¤®ą¤øą„ą¤Æą¤¾ ą¤ ą„ą¤ ą¤Øą¤¹ą„ą¤ ą¤¹ą„ą¤?\ną¤Ŗą¤¹ą¤²ą„ ą¤ą„सा ą¤ą¤°ą¤Øą„ ą¤ą„ ą¤²ą¤æą¤ ą¤ą„प ą¤ą¤°ą„ą¤"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ą¤ą„या ą¤ą„ą¤®ą¤°ą„ ą¤øą„ ą¤ą„ą¤”ą¤¼ą„ ą¤ą„ą¤ ą¤øą¤®ą¤øą„ą¤Æą¤¾ ą¤Øą¤¹ą„ą¤ हą„? ą¤ą¤¾ą¤°ą¤æą¤ ą¤ą¤°ą¤Øą„ ą¤ą„ ą¤²ą¤æą¤ ą¤ą„प ą¤ą¤°ą„ą¤."</string> @@ -89,7 +97,7 @@ <string name="letterbox_education_got_it" msgid="4057634570866051177">"ą¤ ą„ą¤ हą„"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ą¤ą¤¼ą„यादा ą¤ą¤¾ą¤Øą¤ą¤¾ą¤°ą„ ą¤ą„ लिठबऔ़ा ą¤ą¤°ą„ą¤."</string> <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ą¤¬ą„ą¤¹ą¤¤ą¤° ą¤µą„ą¤Æą„ ą¤Ŗą¤¾ą¤Øą„ ą¤ą„ ą¤²ą¤æą¤ ą¤ą¤Ŗą„ą¤²ą¤æą¤ą„शन ą¤ą„ ą¤°ą„ą¤øą„ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą¤Øą¤¾ हą„?"</string> - <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ą¤øą„ą¤ą„ą¤°ą„ą¤Ø पर ą¤ą¤Ŗą„ą¤²ą¤æą¤ą„शन ą¤ą¤¾ ą¤¬ą„ą¤¹ą¤¤ą¤° ą¤µą„ą¤Æą„ ą¤Ŗą¤¾ą¤Øą„ ą¤ą„ ą¤²ą¤æą¤ ą¤ą¤øą„ ą¤°ą„ą¤øą„ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą„ą¤. ą¤¹ą¤¾ą¤²ą¤¾ą¤ą¤ą¤æ, ą¤ą¤Ŗą¤Øą„ ą¤ą„ बदलाव ą¤øą„ą¤µ ą¤Øą¤¹ą„ą¤ ą¤ą¤æą¤ ą¤¹ą„ą¤ या ą¤
ब ą¤¤ą¤ ą¤ą„ ą¤ą¤¾ą¤® ą¤ą¤æą¤ ą¤¹ą„ą¤ ą¤ą¤Øą¤ą¤¾ ą¤”ą„ą¤ą¤¾, ą¤ą¤Ŗą„ą¤²ą¤æą¤ą„शन ą¤°ą„ą¤øą„ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą¤Øą„ पर ą¤®ą¤æą¤ ą¤øą¤ą¤¤ą¤¾ हą„"</string> + <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ą¤øą„ą¤ą„ą¤°ą„ą¤Ø पर ą¤ą¤Ŗą„ą¤²ą¤æą¤ą„शन ą¤ą¤¾ ą¤¬ą„ą¤¹ą¤¤ą¤° ą¤µą„ą¤Æą„ ą¤Ŗą¤¾ą¤Øą„ ą¤ą„ ą¤²ą¤æą¤ ą¤ą¤øą„ ą¤°ą„ą¤øą„ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą„ą¤. ą¤¹ą¤¾ą¤²ą¤¾ą¤ą¤ą¤æ, ą¤ą¤øą¤øą„ ą¤
ब ą¤¤ą¤ ą¤ą¤æą¤Æą¤¾ ą¤ą¤Æą¤¾ ą¤ą¤¾ą¤® ą¤ą¤° ą¤øą„ą¤µ न ą¤ą¤æą¤ ą¤ą¤ बदलाव ą¤®ą¤æą¤ ą¤øą¤ą¤¤ą„ ą¤¹ą„ą¤"</string> <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ą¤°ą¤¦ą„ą¤¦ ą¤ą¤°ą„ą¤"</string> <string name="letterbox_restart_restart" msgid="8529976234412442973">"ą¤°ą„ą¤øą„ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą„ą¤"</string> <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"फिर ą¤øą„ ą¤Ø ą¤¦ą¤æą¤ą¤¾ą¤ą¤"</string> diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml index 2535657c7d86..5f0b866ee4de 100644 --- a/libs/WindowManager/Shell/res/values-hr/strings.xml +++ b/libs/WindowManager/Shell/res/values-hr/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premjesti u gornji desni kut"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premjesti u donji lijevi kut"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premjestite u donji desni kut"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proÅ”irite oblaÄiÄ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sažmite oblaÄiÄ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblaÄiÄ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Zaustavi razgovor u oblaÄiÄima"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ShvaÄam"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblaÄiÄa"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovdje Äe se prikazivati nedavni i odbaÄeni oblaÄiÄi"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"OblaÄiÄi u chatu"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novi se razgovori prikazuju kao ikone u donjem kutu zaslona. Dodirnite da biste ih proÅ”irili ili ih povucite da biste ih odbacili."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Upravljanje oblaÄiÄima u svakom trenutku"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dodirnite ovdje da biste odredili koje aplikacije i razgovori mogu imati oblaÄiÄ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"OblaÄiÄ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"OblaÄiÄ odbaÄen."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da biste ponovo pokrenuli tu aplikaciju kako biste bolje vidjeli."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da biste ponovo pokrenuli tu aplikaciju kako biste bolje vidjeli"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promijeni omjer slike ove aplikacije u postavkama"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promijeni omjer slike"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s fotoaparatom?\nDodirnite za popravak"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rijeÅ”en?\nDodirnite za vraÄanje"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string> diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml index 7566439ec7f8..d89e2923b5fe 100644 --- a/libs/WindowManager/Shell/res/values-hu/strings.xml +++ b/libs/WindowManager/Shell/res/values-hu/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ĆthelyezĆ©s fel Ć©s jobbra"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ĆthelyezĆ©s le Ć©s balra"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ĆthelyezĆ©s le Ć©s jobbra"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> kibontĆ”sa"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ƶsszecsukĆ”sa"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> beĆ”llĆtĆ”sai"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"BuborĆ©k elvetĆ©se"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne jelenjen meg a beszĆ©lgetĆ©s buborĆ©kban"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ćrtem"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nincsenek buborĆ©kok a kƶzelmĆŗltból"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"A legutóbbi Ć©s az elvetett buborĆ©kok itt jelennek majd meg"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"BuborĆ©kokat hasznĆ”ló csevegĆ©s"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Az Ćŗj beszĆ©lgetĆ©sek ikonokkĆ©nt jelennek meg a kĆ©pernyÅ alsó sarkĆ”ban. KoppintĆ”ssal kibonthatja, hĆŗzĆ”ssal pedig elvetheti Åket."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"BuborĆ©kok vezĆ©rlĆ©se bĆ”rmikor"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ide koppintva jelenĆthetÅk meg az alkalmazĆ”sok Ć©s a beszĆ©lgetĆ©sek buborĆ©kkĆ©nt"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"BuborĆ©k"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"KezelĆ©s"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"BuborĆ©k elvetve."</string> - <string name="restart_button_description" msgid="6712141648865547958">"A jobb nĆ©zet Ć©rdekĆ©ben koppintson az alkalmazĆ”s ĆŗjraindĆtĆ”sĆ”hoz."</string> + <string name="restart_button_description" msgid="4564728020654658478">"A jobb nĆ©zet Ć©rdekĆ©ben koppintson az alkalmazĆ”s ĆŗjraindĆtĆ”sĆ”hoz."</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Az app mĆ©retarĆ”nyĆ”t a BeĆ”llĆtĆ”sokban módosĆthatja"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"MĆ©retarĆ”ny módosĆtĆ”sa"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"KamerĆ”val kapcsolatos problĆ©mĆ”ba ütkƶzƶtt?\nKoppintson a megoldĆ”shoz."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavĆtĆ”sa?\nKoppintson a visszaĆ”llĆtĆ”shoz."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problĆ©mĆ”i kamerĆ”val? Koppintson az elvetĆ©shez."</string> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index 2b20870b1048..9c517b29d291 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ÕÕ„Õ²Õ”ÖÕøÕÕ„Õ¬ Õ¾Õ„ÖÖÕ Õ”Õ»"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ÕÕ„Õ²Õ”ÖÕøÕÕ„Õ¬ Õ¶Õ„ÖÖÖÕ Õ±Õ”Õ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ÕÕ„Õ²Õ”ÖÕøÕÕ„Õ¬ Õ¶Õ„ÖÖÖÕ Õ”Õ»"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>. Õ®Õ”Õ¾Õ”Õ¬Õ„Õ¬"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>. Õ®Õ”Õ¬Õ„Õ¬"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ā ÕÆÕ”ÖÕ£Õ”Õ¾ÕøÖÕøÖÕ“Õ¶Õ„Ö"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ÕÕ”ÕÆÕ„Õ¬ Õ”Õ“ÕŗÕ«ÕÆÕØ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ō¶ÖÕøÖÕµÖÕØ Õ¹ÖÕøÖÖÕ”Õ¤ÖÕ„Õ¬ Õ”Õ“ÕŗÕ«ÕÆÕ« ÕæÕ„Õ½ÖÕøÕ¾"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ŌµÕ²Õ”Õ¾"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ō±Õ“ÕŗÕ«ÕÆÕ¶Õ„Ö Õ¹ÕÆÕ”Õ¶"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ō±ÕµÕ½ÕæÕ„Õ² ÕÆÖÕøÖÖÕ”Õ¤ÖÕ¾Õ„Õ¶ Õ¾Õ„ÖÕ»Õ„ÖÕ½ Ö
Õ£ÕæÕ”Õ£ÕøÖÕ®Õ¾Õ”Õ® Ö ÖÕ”ÕÆÕ¾Õ”Õ® Õ”Õ“ÕŗÕ«ÕÆÕ¶Õ„ÖÕØ, ÕøÖÕøÕ¶Ö ÕÆÕÆÕ”ÖÕøÕ²Õ”Õ¶Õ”Ö Õ°Õ„Õ·ÕæÕøÖÕ©ÕµÕ”Õ“Õ¢ Õ¾Õ„ÖÕ”Õ¢Õ”ÖÕ„Õ¬"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Ō¶ÖÕøÖÕµÖÕ« Õ”Õ“ÕŗÕ«ÕÆÕ¶Õ„Ö"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ÕÕøÖ Õ¦ÖÕøÖÕµÖÕ¶Õ„ÖÕØ Õ°Õ”ÕµÕæÕ¶Õ¾ÕøÖÕ“ Õ„Õ¶ ÕøÖÕŗÕ„Õ½ ÕŗÕ”ÕæÕÆÕ„ÖÕ”ÕÆÕ¶Õ„Ö Õ§ÕÆÖÕ”Õ¶Õ« Õ¶Õ„ÖÖÖÕ« Õ”Õ¶ÕÆÕµÕøÖÕ¶ÕøÖÕ“Ö ÕÕŗÕ„ÖÕ Õ¤ÖÕ”Õ¶Ö Õ®Õ”Õ¾Õ”Õ¬Õ„Õ¬ÕøÖ, ÕÆÕ”Õ“ ÖÕ”Õ·Õ„ÖÕ Õ“Õ„ÖÕŖÕ„Õ¬ÕøÖ Õ°Õ”Õ“Õ”ÖÖ"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Ō±Õ“ÕŗÕ«ÕÆÕ¶Õ„ÖÕ« ÕÆÕ”ÖÕ£Õ”Õ¾ÕøÖÕøÖÕ“Õ¶Õ„Ö"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ÕÕŗÕ„Ö Õ”ÕµÕ½ÕæÕ„Õ²Õ ÕØÕ¶ÕæÖÕ„Õ¬ÕøÖ, Õ©Õ„ ÕøÖ Õ°Õ”Õ¾Õ„Õ¬Õ¾Õ”Õ®Õ¶Õ„ÖÕ« Ö Õ¦ÖÕøÖÕµÖÕ¶Õ„ÖÕ« Õ°Õ”Õ“Õ”Ö Õ”Õ“ÕŗÕ«ÕÆÕ¶Õ„Ö ÖÕøÖÖÕ”Õ¤ÖÕ„Õ¬"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ÕÕ²ÕŗÕ»Õ”ÕÆ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ŌæÕ”Õ¼Õ”Õ¾Õ”ÖÕ„Õ¬"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ō±Õ“ÕŗÕ«ÕÆÕØ ÖÕ”ÕÆÕ¾Õ„ÖÖ"</string> - <string name="restart_button_description" msgid="6712141648865547958">"ÕÕŗÕ„ÖÕ Õ°Õ”Õ¾Õ„Õ¬Õ¾Õ”Õ®ÕØ Õ¾Õ„ÖÕ”Õ£ÕøÖÕ®Õ”ÖÕÆÕ„Õ¬ÕøÖ Ö Õ”Õ¾Õ„Õ¬Õ« Õ°Õ”ÖÕ“Õ”Ö ÕæÕ„Õ½Ö ÕØÕ¶ÕæÖÕ„Õ¬ÕøÖ Õ°Õ”Õ“Õ”ÖÖ"</string> + <string name="restart_button_description" msgid="4564728020654658478">"ÕÕŗÕ„ÖÕ Õ°Õ”Õ¾Õ„Õ¬Õ¾Õ”Õ®ÕØ Õ¾Õ„ÖÕ”Õ£ÕøÖÕ®Õ”ÖÕÆÕ„Õ¬ÕøÖ Ö Õ”Õ¾Õ„Õ¬Õ« Õ°Õ”ÖÕ“Õ”Ö ÕæÕ„Õ½Ö ÕØÕ¶ÕæÖÕ„Õ¬ÕøÖ Õ°Õ”Õ“Õ”Ö"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ÕÕøÕÕ„Õ¬ Õ°Õ”Õ¾Õ„Õ¬Õ¾Õ”Õ®Õ« ÕÆÕøÕ²Õ“Õ„ÖÕ« Õ°Õ”ÖÕ”Õ¢Õ„ÖÕ”ÕÆÖÕøÖÕ©ÕµÕøÖÕ¶ÕØ ŌæÕ”ÖÕ£Õ”Õ¾ÕøÖÕøÖÕ“Õ¶Õ„ÖÕøÖÕ“"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ÕÕøÕÕ„Õ¬ Õ¹Õ”ÖÕ„ÖÕ« Õ°Õ”ÖÕ”Õ¢Õ„ÖÕ”ÕÆÖÕøÖÕ©ÕµÕøÖÕ¶ÕØ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ÕÕ„Õ½Õ”ÕÖÕ«ÕÆÕ« Õ°Õ„Õæ ÕÆÕ”ÕŗÕ¾Õ”Õ® ÕÕ¶Õ¤Õ«ÖÕ¶Õ„ÕÖ ÕÆÕ”Õ¶Ö\nÕÕŗÕ„ÖÕ Õ¾Õ„ÖÕ”ÕÆÕ”ÖÕ£Õ”Õ¾ÕøÖÕ„Õ¬ÕøÖ Õ°Õ”Õ“Õ”ÖÖ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ÕÕ°Õ”Õ»ÕøÕ²Õ¾Õ„ÕÖ Õ·ÕæÕÆÕ„Õ¬Ö\nÕÕŗÕ„ÖÕ ÖÕøÖÕøÕÕøÖÕ©ÕµÕøÖÕ¶Õ¶Õ„ÖÕØ Õ¹Õ„Õ²Õ”ÖÕÆÕ„Õ¬ÕøÖ Õ°Õ”Õ“Õ”ÖÖ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ÕÕ„Õ½Õ”ÕÖÕ«ÕÆÕ« Õ°Õ„Õæ ÕÆÕ”ÕŗÕ¾Õ”Õ® ÕÕ¶Õ¤Õ«ÖÕ¶Õ„Ö Õ¹ÕÆÕ”ÕÕ¶Ö ÕÕ”ÕÆÕ„Õ¬ÕøÖ Õ°Õ”Õ“Õ”Ö Õ°ÕŗÕ„ÖÖ"</string> diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml index 5747deb405ab..3dbd75087382 100644 --- a/libs/WindowManager/Shell/res/values-in/strings.xml +++ b/libs/WindowManager/Shell/res/values-in/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pindahkan ke kanan atas"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pindahkan ke kiri bawah"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pindahkan ke kanan bawah"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"luaskan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ciutkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Setelan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Tutup balon"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan gunakan percakapan balon"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Oke"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tidak ada balon baru-baru ini"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Balon yang baru dipakai dan balon yang telah ditutup akan muncul di sini"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat dalam tampilan balon"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Percakapan baru muncul sebagai ikon di bagian pojok bawah layar. Ketuk untuk meluaskan percakapan atau tarik untuk menutup percakapan."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrol balon kapan saja"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ketuk di sini untuk mengelola balon aplikasi dan percakapan"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Ketuk untuk memulai ulang aplikasi ini agar mendapatkan tampilan yang lebih baik."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Ketuk untuk memulai ulang aplikasi ini agar mendapatkan tampilan yang lebih baik"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ubah rasio aspek aplikasi ini di Setelan"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ubah rasio aspek"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Masalah kamera?\nKetuk untuk memperbaiki"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string> @@ -88,7 +96,7 @@ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ketuk dua kali di luar aplikasi untuk mengubah posisinya"</string> <string name="letterbox_education_got_it" msgid="4057634570866051177">"Oke"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Luaskan untuk melihat informasi selengkapnya."</string> - <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Mulai ulang untuk tampilan yang lebih baik?"</string> + <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Mulai ulang untuk melihat tampilan yang lebih baik?"</string> <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Anda dapat memulai ulang aplikasi agar terlihat lebih baik di layar, tetapi Anda mungkin kehilangan progres atau perubahan yang belum disimpan"</string> <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Batal"</string> <string name="letterbox_restart_restart" msgid="8529976234412442973">"Mulai ulang"</string> diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml index 145d26d35b75..2927d6a0d8ec 100644 --- a/libs/WindowManager/Shell/res/values-is/strings.xml +++ b/libs/WindowManager/Shell/res/values-is/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"FƦra efst til hƦgri"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"FƦra neưst til vinstri"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"FƦrưu neưst til hƦgri"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"stƦkka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"minnka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Stillingar <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Loka blƶưru"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ekki setja samtal Ć blƶưru"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ćg skil"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Engar nýlegar blƶưrur"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nýlegar blƶưrur og blƶưrur sem þú hefur lokaư birtast hĆ©r"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Spjall meư blƶưrum"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Ný samtƶl birtast sem tĆ”kn neưst Ć” horni skjĆ”sins. Ćttu til aư stƦkka þau eưa dragưu til aư hunsa þau."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"HƦgt er aư stjórna blƶưrum hvenƦr sem er"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ćttu hĆ©r til aư stjórna þvĆ hvaưa forrit og samtƶl mega nota blƶưrur."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Blaưra"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blƶưru lokaư."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Ćta til aư endurrƦsa forritiư og fĆ” betri sýn."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Ćttu til aư endurrƦsa forritiư og fĆ” betri sýn"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Breyta myndhlutfalli þessa forrits Ć stillingunum"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Breyta myndhlutfalli"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"MyndavĆ©lavesen?\nĆttu til aư breyta stƦrư"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"EnnþÔ vesen?\nĆttu til aư afturkalla"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavĆ©lavesen? Ćttu til aư hunsa."</string> diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml index 025646cbb9cd..938a81466381 100644 --- a/libs/WindowManager/Shell/res/values-it/strings.xml +++ b/libs/WindowManager/Shell/res/values-it/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sposta in alto a destra"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sposta in basso a sinistra"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sposta in basso a destra"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"espandi <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"comprimi <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Impostazioni <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string> @@ -76,17 +78,23 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nessuna bolla recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Le bolle recenti e ignorate appariranno qui"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatta utilizzando le bolle"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Le nuove conversazioni vengono visualizzate sotto forma di icone in un angolo inferiore dello schermo. Tocca per espanderle o trascina per chiuderle."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Gestisci le bolle in qualsiasi momento"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tocca qui per gestire le app e le conversazioni per cui mostrare le bolle"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Tocca per riavviare quest\'app per una migliore visualizzazione."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Tocca per riavviare l\'app e migliorare la visualizzazione"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambia le proporzioni dell\'app nelle Impostazioni"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambia proporzioni"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi con la fotocamera?\nTocca per risolverli"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si ĆØ risolto?\nTocca per ripristinare"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Visualizza più contenuti e fai di più"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trascina in un\'altra app per usare lo schermo diviso"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tocca due volte fuori da un\'app per riposizionarla"</string> - <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string> + <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ok"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Espandi per avere ulteriori informazioni."</string> <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Vuoi riavviare per migliorare la visualizzazione?"</string> <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Puoi riavviare l\'app affinchĆ© venga visualizzata meglio sullo schermo, ma potresti perdere i tuoi progressi o eventuali modifiche non salvate"</string> diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml index bb3845b26a88..aaa0a3866031 100644 --- a/libs/WindowManager/Shell/res/values-iw/strings.xml +++ b/libs/WindowManager/Shell/res/values-iw/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"××¢××Ø× ×פ×× × ×××× ××Ŗ ××¢×××× ×"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"××¢××Ø× ×פ×× × ×ש×××××Ŗ ××Ŗ××Ŗ×× ×"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"××¢××Ø× ×פ×× × ×××× ××Ŗ ××Ŗ××Ŗ×× ×"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"×ר××× ×©× <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"××××ׄ ×©× <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"×××ר××Ŗ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"×”××רת ×××¢×"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"××× ××צ×× ×××¢××Ŗ ×ש×××"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"××× ×Ŗ×"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"××× ×××¢××Ŗ ××××× ×××ר××"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"×××¢××Ŗ ××ר×× ××Ŗ ××××¢××Ŗ ×©× ×”××Ø× ××פ××¢× ×××"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"צ\'×× ××××¢××Ŗ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ש××××Ŗ ××ש××Ŗ ××פ××¢××Ŗ ××”×××× ×פ×× × ××Ŗ××Ŗ×× × ×©× ×××”×. ×פשר ×××§×ש ××× ××ר××× ×××Ŗ× ×× ××ר×ר ××× ××”××ר ×××Ŗ×."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ש×××× ××××¢××Ŗ ××× ×××"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"×פשר ×××§×ש ××× ××× ××§×××¢ ×××× ×פ××קצ×××Ŗ ×ש××××Ŗ ××××× ×××פ××¢ ××××¢××Ŗ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"×××¢×"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"× ××××"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"××××¢× × ×”×ר×."</string> - <string name="restart_button_description" msgid="6712141648865547958">"××× ×ר×××Ŗ ××× ××תר ×ש ×××§×ש ×××פע×× ××Ŗ ××פ××קצ×× ××× ×××ש."</string> + <string name="restart_button_description" msgid="4564728020654658478">"××× ×ר×××Ŗ ××× ××תר ×ש ×××§×ש ×××פע×× ××Ŗ ××פ××קצ×× ××× ×××ש"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"×פשר ××©× ××Ŗ ××Ŗ ×××” ×××××-ר××× ×©× ××פ××קצ×× ××× ×\'×××ר××Ŗ\'"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ש×× ×× ×××” ××××-ר×××"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"××¢×××Ŗ ××צ×××?\n×פשר ×××§×ש ××× ××צע ××Ŗ××× ×××ש"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"×××¢×× ×× × ×¤×Ŗ×Ø×?\n×פשר ×××§×ש ××× ××××ר ×××Ø×”× ××§××××Ŗ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"××× ××¢×××Ŗ ××צ×××? ×פשר ×××§×ש ××× ××”××ר."</string> diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index 6c1bafee9d82..1979fd52e5bb 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -54,7 +54,7 @@ <string name="accessibility_split_top" msgid="2789329702027147146">"äøć«åå²"</string> <string name="accessibility_split_bottom" msgid="8694551025220868191">"äøć«åå²"</string> <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ēęć¢ć¼ćć®ä½æēØ"</string> - <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ēµäŗććć«ćÆćē»é¢ćäøććäøć«ć¹ćÆć¤ćććććć¢ććŖć®ä»»ęć®å “ęććæćććć¾ć"</string> + <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ēµäŗććć«ćÆćē»é¢ćäøććäøć«ć¹ćÆć¤ćććććć¢ććŖć®äøå“ć®ä»»ęć®å “ęććæćććć¾ć"</string> <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ēęć¢ć¼ććéå§ćć¾ć"</string> <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"ēęć¢ć¼ććēµäŗćć¾ć"</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> ć®ććć«ć®čØå®"</string> @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"å³äøć«ē§»å"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"å·¦äøć«ē§»å"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"å³äøć«ē§»å"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ćéćć¾ć"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ćéćć¾ć"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ć®čØå®"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ććć«ćéćć"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ä¼č©±ćććć«ć§č”Øē¤ŗććŖć"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ęčæéććććć«ćÆććć¾ćć"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ęčæč”Øē¤ŗćććććć«ćéććććć«ććććć«č”Øē¤ŗććć¾ć"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ćć£ććć§ććć«ć使ć"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ę°ććä¼č©±ćć¢ć¤ć³ć³ćØćć¦ē»é¢äøéØć«č”Øē¤ŗććć¾ćććæćććććØéćććć©ćć°ćć¦éććććØćć§ćć¾ćć"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ććć«ćÆćć¤ć§ćē®”ēåÆč½"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ććć«ć§č”Øē¤ŗććć¢ććŖćä¼č©±ćē®”ēććć«ćÆćććććæćććć¾ć"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ććć«"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ē®”ē"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ćµćć ććé蔨示ć«ćŖć£ć¦ćć¾ćć"</string> - <string name="restart_button_description" msgid="6712141648865547958">"ćæćććć¦ćć®ć¢ććŖćåčµ·åćććØć蔨示ćé©åć«ćŖćć¾ćć"</string> + <string name="restart_button_description" msgid="4564728020654658478">"ćæćććć¦ćć®ć¢ććŖćåčµ·åćććØć蔨示ćé©åć«ćŖćć¾ć"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ćć®ć¢ććŖć®ć¢ć¹ććÆćęÆć [čØå®] ć§å¤ę“ćć¾ć"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ć¢ć¹ććÆćęÆćå¤ę“"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ć«ć”ć©ć«é¢ććåé”ć®å “åćÆć\nćæćććććØäæ®ę£ć§ćć¾ć"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"äæ®ę£ćććŖćć£ćå “åćÆć\nćæćććććØå
ć«ę»ćć¾ć"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ć«ć”ć©ć«é¢ććåé”ć§ćŖćå “åćÆććæćććććØéćć¾ćć"</string> diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml index e58e67ab36cb..ce1ce6402637 100644 --- a/libs/WindowManager/Shell/res/values-ka/strings.xml +++ b/libs/WindowManager/Shell/res/values-ka/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"įįįįįįįįŖįįįį įįįįį įį įįį įÆįįįį"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"į„įįįįį įį įįį įŖį®įįį įįįįį¢įįį"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"įįįįįįįįŖį. į„įįįįį įį įįį įÆįįįį"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-įį” įįį¤įį įįįįį"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-įį” į©įįįįŖįį"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-įį” įįį įįįį¢į įįį"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"įį£įØį¢įį” įįį®į£į įį"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"įįįį į«įįįį” į”įį£įį įį” įį£įØį¢įįį"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"įįį”įįįįįį"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"įįįį įį įį” įįįįį§įįįįį£įį įį£įØį¢įįį įį įį įį”"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"įį„ įįįįį©įįįįį įįįįįį įįįįįįį įį£įØį¢įįį įį į£įį į§įį¤įįį įį£įØį¢įįį"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"į©įįį įį£įØį¢įįįį” įįįįį§įįįįįį"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"įį®įįį į”įį£įį įįį į®įį¢į£įįį” į”įį®įį įįįįį©įįįįį įį„įįįį įįį įįįį” į„įįįį įį£įį®įįØį. įØįįį®įį įįį”įį¤įį įįįįįįį įį į©įįįįįį įįį įįį”įį®į£į įį."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"įįįį®į¢įįįį” įįįįįį¢į įįįįį įįįįį”įįįį įį įį”"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"įį„ įØįįį®įį įįįį” į”įįįį įįįįį, įᣠį įįįįį įįįįį įį į”įį£įį įįį įįįį®į¢įį”"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"įį£įØį¢į"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"įįį įįį"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"įį£įØį¢į įįįį®į£į į."</string> - <string name="restart_button_description" msgid="6712141648865547958">"įØįįį®įį, į įį įįįįį¢įįį įįį įį” įįį į£įįįįį”į į®įįįį”įįįį”."</string> + <string name="restart_button_description" msgid="4564728020654658478">"įØįį®įįįį įįįįį¢įįį įįį įį” įįį į£įįįįį”į į®įįįį” įįį”įį¦įįįį"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"įØįįŖįįįįį įį įįįį” įįįįį¤įį įįįį įįį įįįį¢į įįįįįį"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"įįįįį¤įį įįįįį” įØįįŖįįį"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"įįįįį įį įį įįįįįįįį įį„įį”?\nįØįįį®įį įįįįį”įį”į¬įį įįįįį"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"įį įįįįį”į¬įį įį?\nįØįįį®įį į¬įįį įįį į”įįį” įįį”įįį į£įįįįįį"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"įįįįį įį” įį įįįįįįįį įį įį„įį”? įØįįį®įį į£įį į§įį¤įį”įįįį”."</string> diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml index 7c9120e22b30..b4b05072f4a4 100644 --- a/libs/WindowManager/Shell/res/values-kk/strings.xml +++ b/libs/WindowManager/Shell/res/values-kk/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ŠŠ¾ŅаŃŃ Š¾Ņ£ жаŅŅŠ° Š¶ŃŠ»Š¶ŃŃŃ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Š¢Ó©Š¼ŠµŠ½Š³Ń ŃŠ¾Š» жаŅŅŠ° Š¶ŃŠ»Š¶ŃŃŃ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Š¢Ó©Š¼ŠµŠ½Š³Ń Š¾Ņ£ жаŅŅŠ° Š¶ŃŠ»Š¶ŃŃŃ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: жаŃ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: жиŃ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ŠæŠ°ŃŠ°Š¼ŠµŃŃŠ»ŠµŃŃ"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ŅŠ°Š»ŅŃŠ¼Š°Š»Ń Ń
Š°Š±Š°ŃŠ“Ń Š¶Š°Š±Ń"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ÓŅ£Š³ŃŠ¼ŠµŠ½ŃŅ£ ŅŠ°Š»ŅŃŠ¼Š° Ń
абаŃŃ ŠŗÓ©ŃŃŠµŃŃŠ»Š¼ŠµŃŃŠ½"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ТүŃŃŠ½ŃŠŗŃŃ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ŠŠ°ŅŃŠ½Š“аŅŃ ŅŠ°Š»ŅŃŠ¼Š° Ń
Š°Š±Š°ŃŠ»Š°Ń жоŅ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ДоңŅŃ Š¶ÓŠ½Šµ Š¶Š°Š±ŃŠ»Ņан ŅŠ°Š»ŅŃŠ¼Š° Ń
Š°Š±Š°ŃŠ»Š°Ń оŃŃ Š¶ŠµŃŠ“е ŠŗÓ©ŃŃŠµŃŃŠ»ŠµŠ“Ń."</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ŅŠ°Š»ŅŃŠ¼Š° Ń
Š°Š±Š°ŃŠ»Š°Ń аŃŅŃŠ»Ń ŃŠ°ŃŃŠ° ŃөйлеŃŃ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ŠŠ°Ņ£Š° ÓŅ£Š³ŃŠ¼ŠµŠ»ŠµŃ ŃŠŗŃаннŃŅ£ ŃÓ©Š¼ŠµŠ½Š³Ń Š±Ó©Š»ŃŠ³ŃнГе белгŃŃŠµ ŃŅÆŃŃŠ½Š“е ŠŗÓ©ŃŃŠµŃŃŠ»ŠµŠ“Ń. ŠŠ»Š°ŃŠ“Ń Š¶Š°Ń ŅÆŃŃŠ½ ŃŅÆŃŃŃŅ£ŃŠ·, ал Š¶Š°Š±Ń ŅÆŃŃŠ½ ŃŅÆŠ¹ŃŠµŅ£ŃŠ·."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ŅŠ°Š»ŅŃŠ¼Š° Ń
Š°Š±Š°ŃŠ»Š°ŃŠ“Ń ŠŗŠµŠ· келген ŃŠ°ŅŃŃŃŠ° баŃŅŠ°ŃŃŅ£ŃŠ·"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ŅŠ°Š»ŅŃŠ¼Š° Ń
Š°Š±Š°ŃŠ“а ŠŗÓ©ŃŃŠµŃŃŠ»ŠµŃŃŠ½ ŅŠ¾Š»Š“Š°Š½Š±Š°Š»Š°Ń Š¼ŠµŠ½ ÓŅ£Š³ŃŠ¼ŠµŠ»ŠµŃŠ“Ń ŃŠµŃŃŠµŃ ŅÆŃŃŠ½ оŃŃ Š¶ŠµŃŠ“Ń ŃŅÆŃŃŃŅ£ŃŠ·."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ŠÓ©ŠæŃŃŃŃŠŗ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ŠŠ°ŃŅŠ°ŃŃ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ŅŠ°Š»ŅŃŠ¼Š° Ń
Š°Š±Š°Ń Š¶Š°Š±ŃŠ»Š“Ń."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Š«Ņ£ŅŠ°Š¹Š»Ń ŠŗÓ©ŃŃŠ½ŃŃŃŃ ŃŠµŃŃŠµŃ ŅÆŃŃŠ½ ŅŠ¾Š»Š“Š°Š½Š±Š°Š½Ń ŃŅÆŃŃŃŠæ, Ó©ŃŃŃŃŠæ ŅŠ¾ŃŃŅ£ŃŠ·."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ŠÓ©ŃŃŠ½ŃŃŃŃ Š¶Š°ŅŃŠ°ŃŃŃ ŅÆŃŃŠ½ оŃŃ ŅŠ¾Š»Š“Š°Š½Š±Š°Š½Ń ŃŅÆŃŃŃŠæ, ŅŠ°Š¹Ńа аŃŃŅ£ŃŠ·."</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŠŃŃ ŅŠ¾Š»Š“анбанŃŅ£ Š°ŃŠ°ŅаŃŃŠ½Š°ŃŃŠ½ ŠæŠ°ŃŠ°Š¼ŠµŃŃŠ»ŠµŃГен өзгеŃŃŃŠ³Šµ болаГŃ."</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ŠŃŠ°ŅŠ°ŃŃŠ½Š°ŃŃŃ Ó©Š·Š³ŠµŃŃŃ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ŠŠ°Š¼ŠµŃаГа ŅŠ°ŃŠµŠ»ŠµŃ ŃŃŅŃŃ Š¼Š°?\nŠÓ©Š½Š“ŠµŃ ŅÆŃŃŠ½ ŃŅÆŃŃŃŅ£ŃŠ·."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ŠÓ©Š½Š“ŠµŠ»Š¼ŠµŠ“Ń Š¼Šµ?\nŅŠ°Š¹ŃаŃŃ ŅÆŃŃŠ½ ŃŅÆŃŃŃŅ£ŃŠ·."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ŠŠ°Š¼ŠµŃаГа ŅŠ°ŃŠµŠ»ŠµŃ ŃŃŅŠæŠ°Š“Ń Š¼Š°? ŠŠ°Š±Ń ŅÆŃŃŠ½ ŃŅÆŃŃŃŅ£ŃŠ·."</string> diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml index 45302677a043..9b0a0dad5782 100644 --- a/libs/WindowManager/Shell/res/values-km/strings.xml +++ b/libs/WindowManager/Shell/res/values-km/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"įįįį¶įįįįøįį
įįįįįįį¶įįį¾įį¶įįįįį¶į"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"įįįį¶įįįįøįį
įįįįįįį¶įįįįįįįį¶įįįįįįā"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"įįįį¶įįįįøįį
įįįįįįį¶įįįįįįāįį¶įįįįį¶į"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"įįįįįøį <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"įįįįį½į <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"įį¶įįįįįį <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"į
įįį¶įį
įįāįįį»į"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"įį»įįįįį į¶įāįį¶įįįįįįį¶āįį¶įįį»į"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"įįįį į¾į"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"įį·įįį¶įāįįį»įāįįįįøįāįį"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"įįį»įįįįįøįā įį·įāįįį»įįįįįį¶įįį·įāāįį¹įāįįįį į¶įāįį
įįøįįį"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"įįįįāįįįįįįį¾āįįįį¶įįį¢įįįįį"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"įį¶įįįįįįį¶įįįįøįįįįį į¶įįį¶įį¼įįįįį¶įįį
įįįį»įįį¶įįįįįįįįį¢įįįįįįįįįįį¢įįįį įį¼įį
į»į
įį¾įįįįøįįįįįøįįį¶įįįįįįį¶įį¶įįįįį į¬į¢į¼į įį¾įįįįøį
įįį¶įį
įįį"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"įįįįįįįįįāįįįį¶įįį¢įįįįįįį
įįįįį¶įįįį¶į"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"į
į»į
įįįįįįįį įį¾įįįįøįįįįįįįįįįįįįįį·įįø įį·įįį¶įįįįįįį¶įįįį¢į¶į
įįįį į¶įįį¶įįįį¶įįį¢įįįįį"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"įįį»į"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"įįįįįįįįį"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"įį¶įį
įįį¶įį
įįāįį¶įįįį
į”į¾įį"</string> - <string name="restart_button_description" msgid="6712141648865547958">"į
į»į
įį¾įįįįøāį
į¶įįįįįį¾įāįįįįįį·įįøāįįįį”į¾įįį·įįįįįį¶įįįį·įįįįį¶įįį¶įįįįįįįįį¾įį"</string> + <string name="restart_button_description" msgid="4564728020654658478">"į
į»į
įį¾įįįįøāį
į¶įįįįįį¾įāįįįįįį·įįøāįįįį”į¾įįį·įįįįįį¶įįįį·įįįįį¶įįį¶įįįįįįįįį¾į"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"įįįį¶įįįįįį¼įįįį¶įį¶įįįāįįįįįįįįįį·įįøįįįįį
įįįį»įįį¶įįįįįį"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"įįįį¼įāāįįį¶įį¶įįį"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"įį¶įįįįį į¶āįį¶įįįįįįįįį¹įāįį¶įįįįį¶į¬?\nį
į»į
įį¾įįįįøāįįįįįįį¶į"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"įį·įįį¶įāįįįįįįį¶įāįįįį į¶įįįįįį¬?\nį
į»į
įį¾įįįįøāįįįį”įį"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"įį·įįį¶įāįįįį į¶įį¶įįįįįįįįį¹įāįį¶įįįįį¶įįį¬? į
į»į
įį¾įįįįøāį
įįį¶įį
įįį"</string> diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml index 2dfbad49bb06..b19978aa09d7 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ಬಲ ą²®ą³ą²²ą³ą²ą²¾ą²ą²ą³ą²ą³ ಸರಿಸಿ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ą²øą³ą²ą³ą²°ą³ą²Øą³āನ ą²ą²” ą²ą³ą²³ą²ą²¾ą²ą²ą³ą²ą³ ಸರಿಸಿ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ą²ą³ą²³ą²ą²æą²Ø ą²¬ą²²ą²ą²¾ą²ą²ą³ą²ą³ ಸರಿಸಿ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą²
ą²Øą³ą²Øą³ ą²µą²æą²øą³ą²¤ą³ą²¤ą²ą³ą²³ą²æą²øą²æ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą²
ą²Øą³ą²Øą³ ą²ą³ą²ą³ą²ą²æą²øą²æ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ą²øą³ą²ą³ą²ą²æą²ą²ą³āą²ą²³ą³"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ą²¬ą²¬ą²²ą³ ą²µą²ą²¾ą²ą³ą²³ą²æą²øą²æ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą²øą²ą²ą²¾ą²·ą²£ą³ą²Æą²Øą³ą²Øą³ ą²¬ą²¬ą²²ą³ ą²®ą²¾ą²”ą²¬ą³ą²”ಿ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą²
ą²°ą³ą²„ವಾಯಿತą³"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ą²Æą²¾ą²µą³ą²¦ą³ ą²ą²¤ą³ą²¤ą³ą²ą²æą²Ø ą²¬ą²¬ą²²ą³ą²øą³ ą²ą²²ą³ą²²"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ą²ą²¤ą³ą²¤ą³ą²ą²æą²Ø ą²¬ą²¬ą²²ą³ą²øą³ ą²®ą²¤ą³ą²¤ą³ ą²µą²ą²¾ą²ą³ą²³ą²æą²øą²æą²¦ ą²¬ą²¬ą²²ą³ą²øą³ ą²ą²²ą³ą²²ą²æ ą²ą³ą²ą²°ą²æą²øą³ą²¤ą³ą²¤ą²µą³"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ą²¬ą²¬ą²²ą³ą²øą³ ಬಳಸಿ ą²ą²¾ą²ą³ ಮಾಔಿ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ą²¹ą³ą²ø ą²øą²ą²ą²¾ą²·ą²£ą³ą²ą²³ą³ ą²Øą²æą²®ą³ą²® ą²øą³ą²ą³ą²°ą³ą²Øą³ ą²ą³ą²³ą²ą²æą²Ø ą²®ą³ą²²ą³ą²Æą²²ą³ą²²ą²æ ą²ą²ą²¾ą²Øą³āą²ą²³ą²¾ą²ą²æ ą²ą³ą²ą²°ą²æą²øą³ą²¤ą³ą²¤ą²µą³. ą²µą²æą²øą³ą²¤ą²°ą²æą²øą²²ą³ ą²
ą²µą³ą²ą²³ą²Øą³ą²Øą³ ą²ą³ą²Æą²¾ą²Ŗą³ ಮಾಔಿ ą²
಄ವಾ ą²µą²ą²¾ą²ą³ą²³ą²æą²øą²²ą³ ą²”ą³ą²°ą³ą²Æą²¾ą²ą³ ಮಾಔಿ."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ą²Æą²¾ą²µą³ą²¦ą³ ą²øą²®ą²Æą²¦ą²²ą³ą²²ą²æ ą²¬ą²¬ą²²ą³ą²øą³ ą²
ą²Øą³ą²Øą³ ą²Øą²æą²Æą²ą²¤ą³ą²°ą²æą²øą²æ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ಯಾವ ą²ą³ą²Æą²Ŗą³āą²ą²³ą³ ą²®ą²¤ą³ą²¤ą³ ą²øą²ą²ą²¾ą²·ą²£ą³ą²ą²³ą²Øą³ą²Øą³ ą²¬ą²¬ą²²ą³ ą²®ą²¾ą²”ą²¬ą²¹ą³ą²¦ą³ ą²ą²ą²¬ą³ą²¦ą²Øą³ą²Øą³ ą²Øą²æą²°ą³ą²µą²¹ą²æą²øą²²ą³ ą²ą³ą²Æą²¾ą²Ŗą³ ಮಾಔಿ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲą³"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ą²Øą²æą²°ą³ą²µą²¹ą²æą²øą²æ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ą²¬ą²¬ą²²ą³ ą²µą²ą²¾ą²ą³ą²³ą²æą²øą²²ą²¾ą²ą²æą²¦ą³."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą²ą²¤ą³ą²¤ą²® ą²µą³ą²ą³ą²·ą²£ą³ą²ą²¾ą²ą²æ ą² ą²ą³ą²Æą²Ŗą³ ą²
ą²Øą³ą²Øą³ ą²®ą²°ą³ą²Ŗą³ą²°ą²¾ą²°ą²ą²ą²æą²øą²²ą³ ą²ą³ą²Æą²¾ą²Ŗą³ ಮಾಔಿ."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą²ą²¤ą³ą²¤ą²® ą²µą³ą²ą³ą²·ą²£ą³ą²ą²¾ą²ą²æ ą² ą²ą³ą²Æą²Ŗą³ ą²
ą²Øą³ą²Øą³ ą²®ą²°ą³ą²Ŗą³ą²°ą²¾ą²°ą²ą²ą²æą²øą²²ą³ ą²ą³ą²Æą²¾ą²Ŗą³ ಮಾಔಿ"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą²øą³ą²ą³ą²ą²æą²ą²ą³āą²ą²³ą²²ą³ą²²ą²æ ą² ą²ą³ą²Æą²Ŗą³āನ ą²¦ą³ą²¶ą³ą²Æą²¾ą²Øą³ą²Ŗą²¾ą²¤ą²µą²Øą³ą²Øą³ ಬದಲಾಯಿಸಿ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą²¦ą³ą²¶ą³ą²Æą²¾ą²Øą³ą²Ŗą²¾ą²¤ą²µą²Øą³ą²Øą³ ಬದಲಾಯಿಸಿ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ą²ą³ą²Æą²¾ą²®ą²°ą²¾ ą²øą²®ą²øą³ą²Æą³ą²ą²³ą²æą²µą³ą²Æą³?\ną²®ą²°ą³ą²¹ą³ą²ą²¦ą²æą²øą²²ą³ ą²ą³ą²Æą²¾ą²Ŗą³ ಮಾಔಿ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ą²
ą²¦ą²Øą³ą²Øą³ ą²øą²°ą²æą²Ŗą²”ą²æą²øą²²ą²æą²²ą³ą²²ą²µą³?\ną²¹ą²æą²ą²¤ą²æą²°ą³ą²ą²æą²øą²²ą³ ą²ą³ą²Æą²¾ą²Ŗą³ ಮಾಔಿ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ą²ą³ą²Æą²¾ą²®ą²°ą²¾ ą²øą²®ą²øą³ą²Æą³ą²ą²³ą²æą²²ą³ą²²ą²µą³? ą²µą²ą²¾ą²ą³ą²³ą²æą²øą²²ą³ ą²ą³ą²Æą²¾ą²Ŗą³ ಮಾಔಿ."</string> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index 39d717dd461a..af08da9d6e65 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ģ¤ė„øģŖ½ ģėØģ¼ė” ģ“ė"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ģ¼ģŖ½ ķėØģ¼ė” ģ“ė"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ģ¤ė„øģŖ½ ķėØģ¼ė” ģ“ė"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ķ¼ģ¹źø°"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ģ źø°"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ģ¤ģ "</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ėķģ°½ ė«źø°"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ėķ넼 ėķģ°½ģ¼ė” ķģķģ§ ģźø°"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ķģø"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ģµź·¼ ėķģ°½ ģģ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ģµź·¼ ėķ창과 ė“ź° ė«ģ ėķģ°½ģ“ ģ¬źø°ģ ķģė©ėė¤."</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ėķģ°½ģ¼ė” ģ±ķ
ķźø°"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ģ ėķė ķė©“ ķėØģ ģģ“ģ½ģ¼ė” ķģė©ėė¤. ģģ“ģ½ģ ķķģ¬ ķ¼ģ¹ź±°ė ėėź·øķģ¬ ė«ģµėė¤."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ģøģ ė ģ§ ėķģ°½ģ ģ ģ“ķģøģ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ėķģ°½ģ ė§ė¤ ģ ģė ģ±ź³¼ ėķ넼 ź“리ķė ¤ė©“ ģ¬źø°ė„¼ ķķģøģ."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ė²ėø"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ź“리"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ėķģ°½ģ ė«ģģµėė¤."</string> - <string name="restart_button_description" msgid="6712141648865547958">"볓기넼 ź°ģ ķė ¤ė©“ ķķģ¬ ģ±ģ ė¤ģ ģģķ©ėė¤."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ķķė©“ ģ±ģ ė¤ģ ģģķģ¬ ė³“źø°ė„¼ ź°ģ ķ©ėė¤."</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ģ¤ģ ģģ ģ±ģ ź°ė”ģøė” ė¹ģØģ ė³ź²½ķ©ėė¤."</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ź°ė”ģøė” ė¹ģØ ė³ź²½"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"칓ė©ė¼ ė¬øģ ź° ģėģ?\nķ“ź²°ķė ¤ė©“ ķķģøģ."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ķ“ź²°ėģ§ ģģėģ?\nėėė¦¬ė ¤ė©“ ķķģøģ."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"칓ė©ė¼ģ ė¬øģ ź° ģėģ? ė«ģ¼ė ¤ė©“ ķķģøģ."</string> diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index f210ea29da00..be24cefbaf91 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -24,7 +24,7 @@ <string name="pip_menu_title" msgid="5393619322111827096">"ŠŠµŠ½Ń"</string> <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"ДүŃÓ©Ń ŠøŃŠøŠ½Š“еги ŃŅÆŃÓ©Ń Š¼ŠµŠ½ŃŃŃ"</string> <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ā ŃŅÆŃÓ©Ń ŠøŃŠøŠ½Š“еги ŃŅÆŃÓ©Ń"</string> - <string name="pip_notification_message" msgid="8854051911700302620">"ŠŠ³ŠµŃ <xliff:g id="NAME">%s</xliff:g> колГонмоŃŃ Š±ŃŠ» ŃŃŠ½ŠŗŃŠøŃŠ½Ń пайГаланбаŃŃŠ½ Š“ŠµŃŠµŅ£ŠøŠ·, Š¶Ó©Š½Š“Ó©Ó©Š»Ó©ŃŠ“ŅÆ аŃŃŠæ ŃŃŃŃŠæ, Š°Š½Ń Ó©ŃŅÆŃŅÆŠæ коŃŅ£ŃŠ·."</string> + <string name="pip_notification_message" msgid="8854051911700302620">"ŠŠ³ŠµŃ <xliff:g id="NAME">%s</xliff:g> колГонмоŃŃ Š±ŃŠ» ŃŃŠ½ŠŗŃŠøŃŠ½Ń пайГаланбаŃŃŠ½ Š“ŠµŃŠµŅ£ŠøŠ·, ŠæŠ°ŃŠ°Š¼ŠµŃŃŠ»ŠµŃГи аŃŃŠæ ŃŃŃŃŠæ, Š°Š½Ń Ó©ŃŅÆŃŅÆŠæ коŃŅ£ŃŠ·."</string> <string name="pip_play" msgid="3496151081459417097">"ŠŠ¹Š½Š¾ŃŃŃ"</string> <string name="pip_pause" msgid="690688849510295232">"Š¢ŃŠ½Š“ŃŃŃŃ"</string> <string name="pip_skip_to_next" msgid="8403429188794867653">"ŠŠøŠ¹ŠøŠ½ŠŗŠøŃине Ó©ŃŠŗÓ©ŃŅÆŠæ жибеŃŅÆŅÆ"</string> @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ŠŠ¾Š³Š¾ŃŠŗŃ Š¾Ņ£ жакка Š¶ŃлГŃŃŃŃ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төмөнкү ŃŠ¾Š» жакка Š¶ŃлГŃŃŃŃ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төмөнкү оң жакка Š¶ŃлГŃŃŃŃ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> Š¶Š°Š¹ŃŠæ ŠŗÓ©ŃŃÓ©ŃŅÆŅÆ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> Š¶ŃŠ¹ŃŃŃŃŃŃŃ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ŠæŠ°ŃŠ°Š¼ŠµŃŃŠ»ŠµŃŠø"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ŠŠ°Š»ŠŗŃŠæ ŃŃŠŗŠ¼Š° Š±ŠøŠ»Š“ŠøŃŠ¼ŠµŠ½Šø жабŃŃ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ŠŠ°Š·ŃŃŃŃŠ“а ŠŗŠ°Š»ŠŗŃŠæ ŃŃŠŗŠ¼Š° Š±ŠøŠ»Š“ŠøŃŠ¼ŠµŠ»ŠµŃ ŠŗÓ©ŃүнбөŃүн"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ТүŃүнГүм"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ŠŠ·ŃŃŃŠ½Ńа ŃŃ Š½ŠµŃŃŠµ жок"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ŠŠŗŃŃŠŗŃ жана Š¶Š°Š±Ńлган ŠŗŠ°Š»ŠŗŃŠæ ŃŃŠŗŠ¼Š° Š±ŠøŠ»Š“ŠøŃŠ¼ŠµŠ»ŠµŃ ŃŃŃŠ» Š¶ŠµŃŠ“е ŠŗÓ©ŃүнөŃ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ŠŠ°Š»ŠŗŃŠæ ŃŃŠŗŠ¼Š° Š±ŠøŠ»Š“ŠøŃŠ¼ŠµŠ»ŠµŃ Š°ŃŠŗŃŠ»ŃŃ Š¼Š°ŠµŠŗŃŠµŃŅÆŅÆ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ŠŠ°Ņ£Ń ŃүйлөŃŅÆŅÆŠ»Ó©Ń ŃŠŗŃанŃŅ£ŃŠ·Š“ŃŠ½ Ńөмөнкү бŃŃŃŃŠ½Š“а ŃŅÆŃÓ©ŃŃÓ©Š»Ó©Ń ŠŗŠ°ŃŠ°ŃŃ ŠŗÓ©ŃүнөŃ. ŠŠ»Š°ŃŠ“Ń Š¶Š°Š¹ŃŠæ ŠŗÓ©ŃŃÓ©ŃŅÆŅÆ ŅÆŃүн ŃŠ°ŠæŃап же ŃŠµŃке кагŃŃ ŅÆŃүн ŃүйŃөңүз."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ŠŠ°Š»ŠŗŃŠæ ŃŃŠŗŠ¼Š° Š±ŠøŠ»Š“ŠøŃŠ¼ŠµŠ»ŠµŃГи каалаган ŃŠ±Š°ŠŗŃа көзөмөлГөңүз"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ŠŠ°Š»ŠŗŃŠæ ŃŃŠŗŠ¼Š° Š±ŠøŠ»Š“ŠøŃŠ¼Šµ ŃŅÆŃүнГө ŠŗÓ©Ńүнө ŃŃŃŠ³Š°Š½ ŠŗŠ¾Š»Š“Š¾Š½Š¼Š¾Š»Š¾Ń Š¼ŠµŠ½ŠµŠ½ Š¼Š°ŠµŠŗŃŠµŃГи ŃŠ°Š½Š“оо ŅÆŃүн Š±ŃŠ» Š¶ŠµŃŠ“Šø ŃŠ°ŠæŃŠ°Ņ£ŃŠ·"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ŠÓ©Š±ŅÆŠŗ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ŠŠ°ŃкаŃŃŃ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ŠŠ°Š»ŠŗŃŠæ ŃŃŠŗŠ¼Š° Š±ŠøŠ»Š“ŠøŃŠ¼Šµ Š¶Š°Š±ŃŠ»Š“Ń."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ŠŠ°ŠŗŃŃŃŠ°Š°Šŗ ŠŗÓ©ŃŅÆŅÆ ŅÆŃүн Š±ŃŠ» ŠŗŠ¾Š»Š“Š¾Š½Š¼Š¾Š½Ń Ó©ŃŅÆŃŅÆŠæ күйгүзүңүз. ŠŠ» ŅÆŃүн ŃŠ°ŠæŃап коŃŅ£ŃŠ·."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ŠŠ°ŠŗŃŃŃŠ°Š°Šŗ ŠŗÓ©ŃŅÆŅÆ ŅÆŃүн Š±ŃŠ» ŠŗŠ¾Š»Š“Š¾Š½Š¼Š¾Š½Ń Ó©ŃŅÆŃŅÆŠæ күйгүзүңүз. ŠŠ» ŅÆŃүн ŃŠ°ŠæŃап коŃŅ£ŃŠ·"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŠŃŠ» ŠŗŠ¾Š»Š“Š¾Š½Š¼Š¾Š½ŃŠ½ ŃŠ°ŃŠ°ŠæŃŠ°ŃŃŠ½Ńн ŠŗŠ°ŃнаŃŃŠ½ ŠæŠ°ŃŠ°Š¼ŠµŃŃŠ»ŠµŃГен өзгөŃŃŅÆŅÆ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Š¢Š°ŃŠ°ŠæŃŠ°ŃŠ“ŃŠ½ ŠŗŠ°ŃŠ½Š°ŃŃŠ½ өзгөŃŃŅÆŅÆ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ŠŠ°Š¼ŠµŃаГа Š¼Š°ŃŠµŠ»ŠµŠ»ŠµŃ келип ŃŃŠŗŃŃŠ±Ń?\nŠŅ£Š“оо ŅÆŃүн ŃŠ°ŠæŃŠ°Ņ£ŃŠ·"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ŠŅ£Š“олгон жокпŃ?\nŠŃŃŠŗŠ° ŠŗŠ°Š¹ŃŠ°ŃŃŃ ŅÆŃүн ŃŠ°ŠæŃŠ°Ņ£ŃŠ·"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ŠŠ°Š¼ŠµŃаГа Š¼Š°Ńеле жокпŃ? ŠŃŠøŠ±Š°ŃŠ³Š° албоо ŅÆŃүн ŃŠ°ŠæŃŠ°Ņ£ŃŠ·."</string> diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml index a25699fa4d10..41219eec986c 100644 --- a/libs/WindowManager/Shell/res/values-lo/strings.xml +++ b/libs/WindowManager/Shell/res/values-lo/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ąŗą»ąŗ²ąŗąŗąŗ§ąŗ²ą»ąŗąŗ“ąŗ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ąŗą»ąŗ²ąŗąŗą»ąŗ²ąŗąŗ„ąŗøą»ąŗ”"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ąŗą»ąŗ²ąŗąŗąŗ§ąŗ²ąŗ„ąŗøą»ąŗ”"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ąŗąŗ°ąŗ«ąŗąŗ²ąŗ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ąŗ«ąŗą»ą» <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ąŗ„ąŗ»ąŗ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ąŗąŗ²ąŗąŗąŗ±ą»ąŗąŗą»ąŗ² <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ąŗąŗ“ąŗąŗąŗąŗą»ąŗ§ą»"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ąŗ¢ą»ąŗ²ą»ąŗą»ąŗąŗąŗą»ąŗąŗąŗ²ąŗąŗŖąŗ»ąŗąŗąŗ°ąŗąŗ²"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą»ąŗąŗ»ą»ąŗ²ą»ąŗą»ąŗ„ą»ąŗ§"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ąŗą»ą»ąŗ”ąŗµąŗąŗąŗąŗ«ąŗ¼ą»ąŗ²ąŗŖąŗøąŗ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ąŗąŗąŗąŗ«ąŗ¼ą»ąŗ²ąŗŖąŗøąŗ ą»ąŗ„ąŗ° ąŗąŗąŗąŗąŗµą»ąŗąŗ“ąŗą»ąŗąŗąŗ°ąŗąŗ²ąŗąŗ»ąŗąŗ¢ąŗ¹ą»ąŗą»ąŗąŗąŗąŗµą»"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ąŗŖąŗ»ąŗąŗąŗ°ąŗąŗ²ą»ąŗąŗą»ąŗą»ąŗąŗąŗ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ąŗąŗ²ąŗąŗŖąŗ»ąŗąŗąŗ°ąŗąŗ²ą»ą»ą»ą»ąŗąŗ°ąŗąŗ²ąŗąŗ»ąŗą»ąŗąŗ±ąŗą»ąŗąŗąŗąŗąŗ¢ąŗ¹ą»ąŗ”ąŗøąŗ”ąŗ„ąŗøą»ąŗ”ąŗŖąŗøąŗąŗąŗąŗą»ą»ąŗ²ąŗą»ąŗąŗąŗąŗą»ąŗ²ąŗ. ą»ąŗąŗ°ą»ąŗąŗ·ą»ąŗąŗąŗ°ąŗ«ąŗąŗ²ąŗ ຫຼື ąŗ„ąŗ²ąŗą»ąŗąŗ·ą»ąŗąŗąŗ“ąŗą»ąŗ§ą»."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ąŗąŗ§ąŗąŗąŗøąŗ”ąŗąŗąŗą»ąŗą»ąŗąŗøąŗą»ąŗ§ąŗ„ąŗ²"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ą»ąŗąŗ°ąŗą»ąŗąŗąŗąŗµą»ą»ąŗąŗ·ą»ąŗąŗąŗ±ąŗąŗąŗ²ąŗą»ąŗąŗ±ąŗ ą»ąŗ„ąŗ° ąŗąŗ²ąŗąŗŖąŗ»ąŗąŗąŗ°ąŗąŗ²ąŗąŗµą»ąŗŖąŗ²ąŗ”ąŗ²ąŗąŗŖąŗ°ą»ąŗąŗą»ąŗąŗ±ąŗą»ąŗąŗąŗąŗąŗą»ąŗą»"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ąŗąŗąŗ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ąŗąŗ±ąŗąŗąŗ²ąŗ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ąŗąŗ“ąŗ Bubble ą»ąŗŖą»ą»ąŗ„ą»ąŗ§."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą»ąŗąŗ°ą»ąŗąŗ·ą»ąŗąŗ£ąŗµąŗŖąŗ°ąŗąŗ²ąŗą»ąŗąŗ±ąŗąŗąŗµą»ą»ąŗąŗ·ą»ąŗąŗ”ąŗøąŗ”ąŗ”ąŗąŗąŗąŗµą»ąŗąŗµąŗąŗ¶ą»ąŗ."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą»ąŗąŗ°ą»ąŗąŗ·ą»ąŗąŗ£ąŗµąŗŖąŗ°ąŗąŗ²ąŗą»ąŗąŗ±ąŗąŗąŗµą»ą»ąŗąŗ·ą»ąŗąŗ”ąŗøąŗ”ąŗ”ąŗąŗąŗąŗµą»ąŗąŗµąŗąŗ¶ą»ąŗ"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ąŗą»ąŗ½ąŗąŗąŗ±ąŗąŗąŗ²ąŗŖą»ąŗ§ąŗąŗąŗąŗą»ąŗąŗ±ąŗąŗąŗµą»ą»ąŗąŗąŗ²ąŗąŗąŗ±ą»ąŗąŗą»ąŗ²"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ąŗą»ąŗ½ąŗąŗąŗ±ąŗąŗąŗ²ąŗŖą»ąŗ§ąŗ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ąŗ”ąŗµąŗąŗ±ąŗąŗ«ąŗ²ąŗą»ąŗąŗąŗą»ąŗ²ąŗąŗ®ąŗ¹ąŗąŗą»?\ną»ąŗąŗ°ą»ąŗąŗ·ą»ąŗąŗąŗ±ąŗą»ą»ą»"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ąŗą»ą»ą»ąŗą»ą»ąŗą»ą»ąŗąŗ”ąŗ±ąŗąŗą»?\ną»ąŗąŗ°ą»ąŗąŗ·ą»ąŗą»ąŗąŗąŗąŗ±ąŗąŗąŗ·ąŗ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ąŗą»ą»ąŗ”ąŗµąŗąŗ±ąŗąŗ«ąŗ²ąŗą»ąŗąŗąŗą»ąŗ²ąŗąŗ®ąŗ¹ąŗąŗą»? ą»ąŗąŗ°ą»ąŗąŗ·ą»ąŗāąŗąŗ“ąŗāą»ąŗ§ą»."</string> diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml index d893fcf21d46..b98fce8f8a3f 100644 --- a/libs/WindowManager/Shell/res/values-lt/strings.xml +++ b/libs/WindowManager/Shell/res/values-lt/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Perkelti ÄÆ virŔų deÅ”inÄje"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Perkelti ÄÆ apaÄiÄ
kairÄje"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Perkelti ÄÆ apaÄiÄ
deÅ”inÄje"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"iÅ”skleisti ā<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ā"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sutraukti ā<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ā"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ā<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>ā nustatymai"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Atsisakyti burbulo"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerodyti pokalbio burbule"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Supratau"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"NÄra naujausių burbulų"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Naujausi ir atsisakyti burbulai bus rodomi Äia"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Pokalbis naudojant burbulus"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nauji pokalbiai rodomi kaip piktogramos apatiniame ekrano kampe. Palieskite piktogramÄ
, jei norite iÅ”plÄsti pokalbÄÆ, arba nuvilkite, kad atsisakytumÄte."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bet kada valdyti burbulus"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"PalietÄ Äia valdykite, kurie pokalbiai ir programos gali bÅ«ti rodomi burbuluose"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"DebesÄlis"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"DebesÄlio atsisakyta."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Palieskite, kad iÅ” naujo paleistumÄte Å”iÄ
programÄ
ir matytumÄte aiÅ”kiau."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Palieskite, kad iÅ” naujo paleistumÄte Å”iÄ
programÄ
ir matytumÄte aiÅ”kiau"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Pakeiskite Å”ios programos kraÅ”tinių santykÄÆ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Keisti kraÅ”tinių santykÄÆ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"IÅ”kilo problemų dÄl kameros?\nPalieskite, kad pritaikytumÄte iÅ” naujo"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grÄ
žintumÄte"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"NÄra jokių problemų dÄl kameros? Palieskite, kad atsisakytumÄte."</string> diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml index a1fbcddb1e3a..11b645ef80b7 100644 --- a/libs/WindowManager/Shell/res/values-lv/strings.xml +++ b/libs/WindowManager/Shell/res/values-lv/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"PÄrvietot augÅ”pusÄ pa labi"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"PÄrvietot apakÅ”pusÄ pa kreisi"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"PÄrvietot apakÅ”pusÄ pa labi"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"IzvÄrst ā<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ā"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Sakļaut ā<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ā"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Lietotnes <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iestatÄ«jumi"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"NerÄdÄ«t burbuli"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"NerÄdÄ«t sarunu burbuļos"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Labi"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nav nesen aizvÄrtu burbuļu"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Å eit bÅ«s redzami nesen rÄdÄ«tie burbuļi un aizvÄrtie burbuļi"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"TÄrzÄÅ”ana, izmantojot burbuļus"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Jaunas sarunas tiek parÄdÄ«tas kÄ ikonas jÅ«su ekrÄna apakÅ”ÄjÄ stÅ«rÄ«. Varat pieskarties, lai tÄs izvÄrstu, vai vilkt, lai tÄs noraidÄ«tu."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"PÄrvaldÄ«t burbuļus jebkurÄ laikÄ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Pieskarieties Å”eit, lai pÄrvaldÄ«tu, kuras lietotnes un sarunas var rÄdÄ«t burbulÄ«"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"PÄrvaldÄ«t"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidÄ«ts."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Pieskarieties, lai restartÄtu Å”o lietotni un uzlabotu attÄlojumu."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Pieskarieties, lai restartÄtu Å”o lietotni un uzlabotu attÄlojumu."</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"IestatÄ«jumos mainiet Ŕīs lietotnes malu attiecÄ«bu."</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"MainÄ«t malu attiecÄ«bu"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Vai ir problÄmas ar kameru?\nPieskarieties, lai tÄs novÄrstu."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problÄma netika novÄrsta?\nPieskarieties, lai atjaunotu."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problÄmu ar kameru? Pieskarieties, lai nerÄdÄ«tu."</string> diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml index 427433c7f2d4..ba2c97a4f29f 100644 --- a/libs/WindowManager/Shell/res/values-mk/strings.xml +++ b/libs/WindowManager/Shell/res/values-mk/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ŠŃемеŃŃŠø Š³Š¾ŃŠµ Š“ŠµŃŠ½Š¾"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ŠŃемеŃŃŠø Š“Š¾Š»Ń Š»ŠµŠ²Š¾"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ŠŃемеŃŃŠø Š“Š¾Š»Ń Š“ŠµŃŠ½Š¾"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ŠæŃŠ¾ŃŠøŃŠø <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ŃŠ¾Š±ŠµŃŠø <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ŠŠ¾ŃŃŠ°Š²ŠŗŠø за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ŠŃŃŃŠ»Šø Š±Š°Š»Š¾Š½ŃŠµ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ŠŠµ ŠæŃŠøŠŗŠ°Š¶ŃŠ²Š°Ń Š³Š¾ ŃŠ°Š·Š³Š¾Š²Š¾ŃŠ¾Ń Š²Š¾ Š±Š°Š»Š¾Š½ŃŠøŃа"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Š”ŃŠ°Ńив"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ŠŠµŠ¼Š° Š½ŠµŠ¾Š“Š°Š¼Š½ŠµŃŠ½Šø Š±Š°Š»Š¾Š½ŃŠøŃа"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ŠŠµŠ¾Š“Š°Š¼Š½ŠµŃŠ½ŠøŃе Šø оŃŃŃŠ»ŠµŠ½ŠøŃе Š±Š°Š»Š¾Š½ŃŠøŃа ŃŠµ ŃŠµ ŠæŠ¾ŃŠ°Š²ŃŠ²Š°Š°Ń ŃŃŠŗŠ°"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Š Š°Š·Š³Š¾Š²Š¾Ń ŃŠ¾ Š±Š°Š»Š¾Š½ŃŠøŃа"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ŠŠ¾Š²ŠøŃе ŃŠ°Š·Š³Š¾Š²Š¾ŃŠø ŃŠµ ŠæŠ¾ŃŠ°Š²ŃŠ²Š°Š°Ń ŠŗŠ°ŠŗŠ¾ икони во Š“Š¾Š»Š½ŠøŠ¾Ń Š°Š³Š¾Š» оГ ŠµŠŗŃŠ°Š½Š¾Ń. ŠŠ¾ŠæŃŠµŃŠµ за Га ги ŠæŃŠ¾ŃŠøŃŠøŃе или ŠæŠ¾Š²Š»ŠµŃŠµŃе за Га ги оŃŃŃŠ»ŠøŃе."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ŠŠ¾Š½ŃŃŠ¾Š»ŠøŃаŃŃŠµ ги Š±Š°Š»Š¾Š½ŃŠøŃŠ°Ńа во ŃŠµŠŗŠ¾Šµ Š²ŃŠµŠ¼Šµ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ŠŠ¾ŠæŃŠµŃŠµ ŃŃŠŗŠ° за Га Š¾Š“ŃŠµŠ“ŠøŃŠµ на кои апл. Šø ŃŠ°Š·Š³Š¾Š²Š¾ŃŠø може Га ŃŠµ ŠæŠ¾ŃŠ°Š²Š°Ń Š±Š°Š»Š¾Š½ŃŠøŃа"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ŠŠ°Š»Š¾Š½Ńе"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Š£ŠæŃŠ°Š²ŃваŃŃŠµ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ŠŠ°Š»Š¾Š½ŃŠµŃŠ¾ е оŃŃŃŠ»ŠµŠ½Š¾."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ŠŠ¾ŠæŃŠµŃŠµ за Га ŃŠ° ŃŠµŃŃŠ°ŃŃŠøŃŠ°ŃŠµ Š°ŠæŠ»ŠøŠŗŠ°ŃŠøŃава за ŠæŠ¾Š“Š¾Š±Š°Ń ŠæŃŠøŠŗŠ°Š·."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ŠŠ¾ŠæŃŠµŃŠµ за Га ŃŠ° ŃŠµŃŃŠ°ŃŃŠøŃŠ°ŃŠµ Š°ŠæŠ»ŠøŠŗŠ°ŃŠøŃава за ŠæŠ¾Š“Š¾Š±Š°Ń ŠæŃŠøŠŗŠ°Š·"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŠŃомени го ŃŠ¾Š¾Š“Š½Š¾ŃŠ¾Ń на Š°ŠæŠ»ŠøŠŗŠ°ŃŠøŃава во āŠŠ¾ŃŃŠ°Š²ŠŗŠøā"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ŠŃŠ¾Š¼ŠµŠ½ŃŠ²Š°Ńе на ŃŠ¾Š¾Š“Š½Š¾ŃŠ¾Ń"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ŠŃоблеми ŃŠ¾ ŠŗŠ°Š¼ŠµŃŠ°Ńа?\nŠŠ¾ŠæŃŠµŃŠµ за Га ŃŠµ ŃŠ¾Š²ŠæŠ°Š“не ŠæŠ¾Š²ŃŠ¾Ńно"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ŠŠµ ŃŠµ ŠæŠ¾ŠæŃŠ°Š²Šø?\nŠŠ¾ŠæŃŠµŃŠµ за Š²ŃŠ°ŃŠ°Ńе"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ŠŠµŠ¼Š° ŠæŃŠ¾Š±Š»ŠµŠ¼Šø ŃŠ¾ ŠŗŠ°Š¼ŠµŃŠ°Ńа? ŠŠ¾ŠæŃŠµŃŠµ за оŃŃŃŠ»Š°Ńе."</string> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index 5cca248e0dd4..8a95d7ef157c 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ą“®ąµą“ą“³ą“æąµ½ ą“µą“²ą“¤ąµą“ą“¾ą“ą“¤ąµą“¤ąµą“ąµą“ąµ ą“Øąµą“ąµą“ąµą“"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ą“ąµą“µą“ąµ ą“ą“ą“¤ąµą“ą“¾ą“ą“¤ąµą“¤ąµą“ąµą“ąµ ą“Øąµą“ąµą“ąµą“"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ą“ąµą“µą“ąµ ą“µą“²ą“¤ąµą“ą“¾ą“ą“¤ąµą“¤ąµą“ąµą“ąµ ą“Øąµą“ąµą“ąµą“"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą“µą“æą“ą“øą“æą“Ŗąµą“Ŗą“æą“ąµą“ąµą“"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą“ąµą“°ąµą“ąµą“ąµą“"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ą“ąµą“°ą“®ąµą“ą“°ą“£ą“"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ą“¬ą“¬ą“æąµ¾ ą“”ą“æą“øąµą“®ą“æą“øąµ ą“ąµą“Æąµą“Æąµ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą“øą“ą“ą“¾ą“·ą“£ą“ ą“¬ą“¬ą“æąµ¾ ą“ąµą“Æąµą“Æą“°ąµą“¤ąµ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą“®ą“Øą“øąµą“øą“æą“²ą“¾ą“Æą“æ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ą“
ą“ąµą“¤ąµą“¤ą“æą“ąµą“Æąµą“³ąµą“³ ą“¬ą“¬ą“æą“³ąµą“ąµ¾ ą“ą“Øąµą“Øąµą“®ą“æą“²ąµą“²"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ą“
ą“ąµą“¤ąµą“¤ą“æą“ąµą“Æąµą“³ąµą“³ ą“¬ą“¬ą“æą“³ąµą“ąµ¾, ą“”ą“æą“øąµą“®ą“æą“øąµ ą“ąµą“Æąµą“¤ ą“¬ą“¬ą“æą“³ąµą“ąµ¾ ą“ą“Øąµą“Øą“æą“µ ą“ą“µą“æą“ąµ ą“¦ąµą“¶ąµą“Æą“®ą“¾ą“µąµą“"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ą“¬ą“¬ą“æą“³ąµą“ąµ¾ ą“ą“Ŗą“Æąµą“ą“æą“ąµą“ąµ ą“ą“¾ą“±ąµą“±ąµ ą“ąµą“Æąµą“Æąµą“"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ą“Ŗąµą“¤ą“æą“Æ ą“øą“ą“ą“¾ą“·ą“£ą“ąµą“ąµ¾, ą“Øą“æą“ąµą“ą“³ąµą“ąµ ą“øąµą“ąµą“°ąµą“Øą“æą“Øąµą“±ąµ ą“¤ą“¾ą““ąµ ą“®ąµą“²ą“Æą“æąµ½ ą“ą“ąµą“ą“£ąµą“ą“³ą“¾ą“Æą“æ ą“¦ąµą“¶ąµą“Æą“®ą“¾ą“ąµą“. ą“µą“æą“ą“øą“æą“Ŗąµą“Ŗą“æą“ąµą“ą“¾ąµ» ą“
ą“µą“Æą“æąµ½ ą“ą“¾ą“Ŗąµą“Ŗąµ ą“ąµą“Æąµą“Æąµą“ ą“
ą“²ąµą“²ąµą“ąµą“ą“æąµ½ ą“”ą“æą“øąµāą“®ą“æą“øąµ ą“ąµą“Æąµą“Æą“¾ąµ» ą“µą“²ą“æą“ąµą“ą“æą“ąµą“."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ą“¬ą“¬ą“æą“³ąµą“ąµ¾ ą“ą“¤ąµą“øą“®ą“Æą“¤ąµą“¤ąµą“ ą“Øą“æą“Æą“Øąµą“¤ąµą“°ą“æą“ąµą“ąµą“"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ą“ą“¤ąµą“ąµą“ąµ ą“ą“Ŗąµą“Ŗąµą“ą“³ąµą“ ą“øą“ą“ą“¾ą“·ą“£ą“ąµą“ą“³ąµą“ ą“¬ą“¬ą“æąµ¾ ą“ąµą“Æąµą“Æą“¾ą“Øą“¾ą“ąµą“®ąµą“Øąµą“Øą“¤ąµ ą“®ą“¾ą“Øąµą“ąµ ą“ąµą“Æąµą“Æą“¾ąµ» ą“ą“µą“æą“ąµ ą“ą“¾ą“Ŗąµą“Ŗąµ ą“ąµą“Æąµą“Æąµą“"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ą“¬ą“¬ą“æąµ¾"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ą“®ą“¾ą“Øąµą“ąµ ą“ąµą“Æąµą“Æąµą“"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ą“¬ą“¬ą“æąµ¾ ą“”ą“æą“øąµą“®ą“æą“øąµ ą“ąµą“Æąµą“¤ąµ."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą“®ą“æą“ą“ąµą“ ą“ą“¾ą““ąµāą“ą“Æąµāą“ąµą“ą“¾ą“Æą“æ ą“ ą“ą“Ŗąµą“Ŗąµ ą“±ąµą“øąµāą“±ąµą“±ą“¾ąµ¼ą“ąµą“ąµ ą“ąµą“Æąµą“Æą“¾ąµ» ą“ą“¾ą“Ŗąµą“Ŗąµ ą“ąµą“Æąµą“Æąµą“."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą“®ą“æą“ą“ąµą“ ą“ą“¾ą““ąµāą“ą“Æąµāą“ąµą“ą“¾ą“Æą“æ ą“ ą“ą“Ŗąµą“Ŗąµ ą“±ąµą“øąµāą“±ąµą“±ą“¾ąµ¼ą“ąµą“ąµ ą“ąµą“Æąµą“Æą“¾ąµ» ą“ą“¾ą“Ŗąµą“Ŗąµ ą“ąµą“Æąµą“Æąµą“"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą“ ą“ą“Ŗąµą“Ŗą“æą“Øąµą“±ąµ ą“µąµą“ąµą“·ą“£ ą“
ą“Øąµą“Ŗą“¾ą“¤ą“, ą“ąµą“°ą“®ąµą“ą“°ą“£ą“¤ąµą“¤ą“æąµ½ ą“®ą“¾ą“±ąµą“±ąµą“"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą“µąµą“ąµą“·ą“£ ą“
ą“Øąµą“Ŗą“¾ą“¤ą“ ą“®ą“¾ą“±ąµą“±ąµą“"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ą“ąµą“Æą“¾ą“®ą“± ą“Ŗąµą“°ą“¶ąµą“Øą“ąµą“ą“³ąµą“£ąµą“ąµ?\ną“¶ą“°ą“æą“Æą“¾ą“ąµą“ą“¾ąµ» ą“ą“¾ą“Ŗąµą“Ŗąµ ą“ąµą“Æąµą“Æąµą“"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ą“
ą“¤ąµ ą“Ŗą“°ą“æą“¹ą“°ą“æą“ąµą“ą“æą“²ąµą“²ąµ?\ną“Ŗąµą“Øą“ą“øąµą“„ą“¾ą“Ŗą“æą“ąµą“ą“¾ąµ» ą“ą“¾ą“Ŗąµą“Ŗąµ ą“ąµą“Æąµą“Æąµą“"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ą“ąµą“Æą“¾ą“®ą“±ą“¾ ą“Ŗąµą“°ą“¶ąµą“Øą“ąµą“ą“³ąµą“Øąµą“Øąµą“®ą“æą“²ąµą“²ąµ? ą“Øą“æą“°ą“øą“æą“ąµą“ą“¾ąµ» ą“ą“¾ą“Ŗąµą“Ŗąµ ą“ąµą“Æąµą“Æąµą“."</string> diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml index 72e54fcf8711..bead7fe0d1c7 100644 --- a/libs/WindowManager/Shell/res/values-mn/strings.xml +++ b/libs/WindowManager/Shell/res/values-mn/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ŠŠ°ŃŃŃŠ½ Š“ŃŃŃ Š·Ó©Ó©Ń
"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ŠŅÆŅÆŠ½ Š“Š¾Š¾Ń Š·Ó©Ó©Ń
"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ŠŠ°ŃŃŃŠ½ Š“Š¾Š¾Ń Š·Ó©Ó©Ń
"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-г Š“ŃŠ»Š³ŃŃ
"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-г Ń
ŃŃŠ°Š°Ń
"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-н ŃŠ¾Ń
ŠøŃŠ³Š¾Š¾"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ŠÓ©Š¼Š±Ó©Š»Š³ŠøŠ¹Š³ Ń
ааŃ
"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Š„Š°ŃŠøŠ»Ńан ŃŃŠøŠ°Š³ бүү бөмбөлөг болго"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ŠŠ¹Š»Š³Š¾Š»Š¾Š¾"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ДаŃŃ
Š½Ń Š±Ó©Š¼Š±Ó©Š»Ó©Š³ алга байна"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ДаŃŃ
Š½Ń Š±Ó©Š¼Š±Ó©Š»Š³ŅÆŅÆŠ“ болон ŅÆŠ» Ń
ŃŃŃŠ³ŃŃŃŠ½ бөмбөлгүүГ ŃŠ½Š“ Ń
Š°ŃŠ°Š³Š“ана"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ŠÓ©Š¼Š±Ó©Š»Š³ŅÆŅÆŠ“ Š°ŃŠøŠ³Š»Š°Š½ ŃŠ°ŃлаŃ
"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ŠØŠøŠ½Ń Ń
Š°ŃŠøŠ»Ńан ŃŃŠøŠ°Š½ŃŃŠ“ ŃŠ°Š½Ń Š“ŃŠ»Š³ŃŃŠøŠ¹Š½ ГооГ Š±ŃŠ»Š°Š½Š“ ГүŃŃ ŃŃŠ¼Š“ŃŠ³ Š±Š°Š¹Š“Š»Š°Š°Ń Ń
Š°ŃŠ°Š³Š“ана. Š¢ŃŠ“гŃŃŃŠøŠ¹Š³ Š“ŃŠ»Š³ŃŃ
ийн ŃŃŠ»Š“ ŃŠ¾Š²ŃŠøŃ
ŃŃŠ²ŃŠ» Ń
ааŃ
ŃŠ½ ŃŃŠ»Š“ ŃŠøŃŠ½Ń ŅÆŅÆ."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ŠÓ©Š¼Š±Ó©Š»Š³ŅÆŅÆŠ“ийг Ń
ŅÆŃŃŃŠ½ үеГŃŃ Ń
ŃŠ½Š°Ń
"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ŠÆŠ¼Š°Ń Š°ŠæŠæ болон Ń
Š°ŃŠøŠ»Ńан ŃŃŠøŠ°Š½ŃŃŠ“ Š±Ó©Š¼Š±Ó©Š»Š³Ó©Ó©Ń Ń
Š°ŃŠ°Š³Š“аŃ
ŃŠ³ ŃŠ½Š“ ŃŠ“ŠøŃŠ“ана ŃŃ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ŠÓ©Š¼Š±Ó©Š»Ó©Š³"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Š£Š“ŠøŃŠ“аŃ
"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ŠÓ©Š¼Š±Ó©Š»Š³ŠøŠ¹Š³ ŅÆŠ» Ń
ŃŃŃŠ³ŃŃŃŠ½."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Š„Š°ŃŠ°Š³Š“аŃ
Š±Š°Š¹Š“Š»ŃŠ³ ŃŠ°Š¹Š¶ŃŃŃŠ»Š°Ń
ŃŠ½ ŃŃŠ»Š“ ŃŠ½Ń Š°ŠæŠæŃŠ³ ŃŠ¾Š²Ńиж, ГаŃ
ин ŃŃ
Š»ŅÆŅÆŠ»Š½Ń ŅÆŅÆ."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Š„Š°ŃŠ°Š³Š“аŃ
Š±Š°Š¹Š“Š»ŃŠ³ ŃŠ°Š¹Š¶ŃŃŃŠ»Š°Ń
ŃŠ½ ŃŃŠ»Š“ ŃŠ½Ń Š°ŠæŠæŃŠ³ ŃŠ¾Š²Ńиж, ГаŃ
ин ŃŃ
Š»ŅÆŅÆŠ»Š½Ń ŅÆŅÆ"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŠŠ½Ń Š°ŠæŠæŃŠ½ Ń
аŃŃŃŠ°Š°Š³ ТоŃ
ŠøŃŠ³Š¾Š¾Š½Š“ Ó©Ó©ŃŃŠøŠ»Š½Ó© ŅÆŅÆ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ЄаŃŃŃŠ°Š°Š³ Ó©Ó©ŃŃŠ»Ó©Ń
"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ŠŠ°Š¼ŠµŃŃŠ½ аŃŃŃŠ“ал гаŃŃŠ°Š½ ŃŃ?\nŠŠ°Ń
ин ŃŠ°Š°ŃŃŃŠ»Š°Ń
ŃŠ½ ŃŃŠ»Š“ ŃŠ¾Š²Ńино ŃŃ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг Š·Š°Ńаагүй ŃŃ?\nŠŃŃŠ°Š°Ń
ŃŠ½ ŃŃŠ»Š“ ŃŠ¾Š²Ńино ŃŃ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ŠŠ°Š¼ŠµŃŃŠ½ аŃŃŃŠ“ал байŃ
гүй ŃŃ? ЄааŃ
ŃŠ½ ŃŃŠ»Š“ ŃŠ¾Š²Ńино ŃŃ."</string> diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml index a9e6319a2110..ca0e15a0241b 100644 --- a/libs/WindowManager/Shell/res/values-mr/strings.xml +++ b/libs/WindowManager/Shell/res/values-mr/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"वर ą¤ą¤ą¤µą„ą¤ą¤”ą„ ą¤¹ą¤²ą¤µą¤¾"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ą¤¤ą¤³ą¤¾ą¤¶ą„ ą¤”ą¤¾ą¤µą„ą¤ą¤”ą„ ą¤¹ą¤²ą¤µą¤¾"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ą¤¤ą¤³ą¤¾ą¤¶ą„ ą¤ą¤ą¤µą„ą¤ą¤”ą„ ą¤¹ą¤²ą¤µą¤¾"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¤µą¤æą¤øą„ą¤¤ą¤¾ą¤° ą¤ą¤°ą¤¾"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¤ą„लą„
ą¤Ŗą„ą¤ø ą¤ą¤°ą¤¾"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ą¤øą„ą¤ą¤æą¤ą¤ą„ą¤"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल औिसमिस ą¤ą¤°ą¤¾"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą¤øą¤ą¤ą¤¾ą¤·ą¤£ą¤¾ą¤²ą¤¾ बबल ą¤ą¤°ą„ ą¤Øą¤ą¤¾"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą¤øą¤®ą¤ą¤²ą„"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ą¤
ą¤²ą„ą¤ą¤”ą„ą¤² ą¤ą„ą¤£ą¤¤ą„ą¤¹ą„ बबल ą¤Øą¤¾ą¤¹ą„ą¤¤"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ą¤
ą¤²ą„ą¤ą¤”ą„ą¤² बबल ą¤ą¤£ą¤æ औिसमिस ą¤ą„ą¤²ą„ą¤²ą„ बबल ą¤Æą„ą¤„ą„ ą¤¦ą¤æą¤øą¤¤ą„ą¤²"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"बबल ą¤µą¤¾ą¤Ŗą¤°ą„ą¤Ø ą¤ą„
ą¤ ą¤ą¤°ą¤¾"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ą¤Øą¤µą„ą¤Ø ą¤øą¤ą¤ą¤¾ą¤·ą¤£ą„ ą¤¹ą„ ą¤¤ą„ą¤®ą¤ą„या ą¤øą„ą¤ą„ą¤°ą„ą¤Øą¤ą„या ą¤¤ą¤³ą¤¾ą¤¶ą„ ą¤
ą¤øą¤²ą„ą¤²ą„या ą¤ą„ą¤Ŗą¤±ą„ą¤Æą¤¾ą¤¤ ą¤ą¤Æą¤ą¤Øą¤ą„या ą¤øą„ą¤µą¤°ą„पात दिसतात. ą¤¤ą„ą¤Æą¤¾ą¤ą¤ą¤¾ ą¤µą¤æą¤øą„ą¤¤ą¤¾ą¤° ą¤ą¤°ą¤£ą„ą¤Æą¤¾ą¤øą¤¾ą¤ ą„ ą¤ą„
प ą¤ą¤°ą¤¾ ą¤ą¤æą¤ą¤µą¤¾ ą¤¤ą„ ą¤”ą¤æą¤øą¤®ą¤æą¤ø ą¤ą¤°ą¤£ą„ą¤Æą¤¾ą¤øą¤¾ą¤ ą„ ą¤”ą„ą¤°ą„
ą¤ ą¤ą¤°ą¤¾."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"बबल ą¤ą¤§ą„ą¤¹ą„ ą¤Øą¤æą¤Æą¤ą¤¤ą„रित ą¤ą¤°ą¤¾"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ą¤ą„ą¤£ą¤¤ą„ ą„²ą¤Ŗą„ą¤ø ą¤ą¤£ą¤æ ą¤øą¤ą¤ą¤¾ą¤·ą¤£ą„ बबल ą¤¹ą„ą¤ ą¤¶ą¤ą¤¤ą¤¾ą¤¤ ą¤¹ą„ ą¤µą„ą¤Æą¤µą¤øą„ऄापित ą¤ą¤°ą¤£ą„ą¤Æą¤¾ą¤øą¤¾ą¤ ą„ ą¤Æą„ą¤„ą„ ą¤ą„
प ą¤ą¤°ą¤¾"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ą¤µą„ą¤Æą¤µą¤øą„ऄापित ą¤ą¤°ą¤¾"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल औिसमिस ą¤ą„ला."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą¤
ą¤§ą¤æą¤ ą¤ą¤¾ą¤ą¤ą¤²ą„या ą¤µą„ą¤¹ą„ą¤Æą„ą¤øą¤¾ą¤ ą„ ą¤¹ą„ ą¤
āą„
प ą¤°ą„ą¤øą„ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą¤£ą„ą¤Æą¤¾ą¤ą¤°ą¤æą¤¤ą¤¾ ą¤ą„
प ą¤ą¤°ą¤¾."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą¤
ą¤§ą¤æą¤ ą¤ą¤¾ą¤ą¤ą¤²ą„या ą¤¦ą„ą¤¶ą„ą¤Æą¤¾ą¤øą¤¾ą¤ ą„ ą¤¹ą„ ą¤
āą„
प ą¤°ą„ą¤øą„ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą¤£ą„ą¤Æą¤¾ą¤ą¤°ą¤æą¤¤ą¤¾ ą¤ą„
प ą¤ą¤°ą¤¾"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą¤øą„ą¤ą¤æą¤ą¤ą„ą¤ ą¤®ą¤§ą„ą¤Æą„ या ą„²ą¤Ŗą¤ą¤¾ ą¤ą¤øą„ą¤Ŗą„ą¤ą„ą¤ ą¤°ą„ą¤¶ą„ बदला"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą¤ą¤øą„ą¤Ŗą„ą¤ą„ą¤ ą¤°ą„ą¤¶ą„ बदला"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ą¤ą„
ą¤®ą„ą¤°ą¤¾ą¤¶ą„ ą¤øą¤ą¤¬ą¤ą¤§ą¤æą¤¤ ą¤ą¤¾ą¤¹ą„ ą¤øą¤®ą¤øą„ą¤Æą¤¾ ą¤ą¤¹ą„त ą¤ą¤¾?\ną¤Ŗą„ą¤Øą„हा ą¤«ą¤æą¤ ą¤ą¤°ą¤£ą„ą¤Æą¤¾ą¤øą¤¾ą¤ ą„ ą¤ą„
प ą¤ą¤°ą¤¾"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ą¤Øą¤æą¤°ą¤¾ą¤ą¤°ą¤£ ą¤ą¤¾ą¤²ą„ नाहą„?\ną¤°ą¤æą¤µą„ą¤¹ą¤°ą„ą¤ ą¤ą¤°ą¤£ą„ą¤Æą¤¾ą¤øą¤¾ą¤ ą„ ą¤ą„पया ą¤ą„
प ą¤ą¤°ą¤¾"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ą¤ą„
ą¤®ą„ą¤°ą¤¾ą¤¶ą„ ą¤øą¤ą¤¬ą¤ą¤§ą¤æą¤¤ ą¤ą„ą¤£ą¤¤ą„ą¤Æą¤¾ą¤¹ą„ ą¤øą¤®ą¤øą„ą¤Æą¤¾ ą¤Øą¤¾ą¤¹ą„ą¤¤ ą¤ą¤¾? औिसमिस ą¤ą¤°ą¤£ą„āą¤Æą¤¾ą¤øą¤¾ą¤ ą„ ą¤ą„
प ą¤ą¤°ą¤¾."</string> diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml index b47531711863..fd9fb87b918c 100644 --- a/libs/WindowManager/Shell/res/values-ms/strings.xml +++ b/libs/WindowManager/Shell/res/values-ms/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Alihkan ke atas sebelah kanan"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Alihkan ke bawah sebelah kiri"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Alihkan ke bawah sebelah kanan"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"kembangkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kuncupkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Tetapan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ketepikan gelembung"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan jadikan perbualan dalam bentuk gelembung"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tiada gelembung terbaharu"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Gelembung baharu dan gelembung yang diketepikan akan dipaparkan di sini"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bersembang menggunakan gelembung"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Perbualan baharu dipaparkan sebagai ikon pada penjuru sebelah bawah skrin anda. Ketik untuk mengembangkan perbualan atau seret untuk mengetepikan perbualan."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kawal gelembung pada bila-bila masa"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Ketik di sini untuk mengurus apl dan perbualan yang boleh menggunakan gelembung"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Ketik untuk memulakan semula apl ini untuk mendapatkan paparan yang lebih baik."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Ketik untuk memulakan semula apl ini untuk mendapatkan paparan yang lebih baik"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Tukar nisbah bidang apl ini dalam Tetapan"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tukar nisbah bidang"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Isu kamera?\nKetik untuk memuatkan semula"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string> diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml index cb6a1df95b33..42bce8104a5c 100644 --- a/libs/WindowManager/Shell/res/values-my/strings.xml +++ b/libs/WindowManager/Shell/res/values-my/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"įį¬įįįŗįįįįŗįįįÆį· įį½į¾į±į·įį«"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"įįįŗį”į±į¬įįŗįį¼į±įįįÆį· įį½į¾į±į·įįįŗ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"įį¬į”į±į¬įįŗįį¼į±įįįÆį· įį½į¾į±į·įį«"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> įįᯠįį»į²į·įįįŗ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> įįᯠįį»įÆį¶į·įįįŗ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> įįįŗįįįŗįį»į¬įø"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"įį°įį±į¬įįŗįøįį½įįŗ įįįŗįįįŗ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"į
įį¬įøįįįÆįįŗįøįįᯠįį°įį±į¬įįŗįøįį½įįŗ įįį¼įÆįįÆįįŗįį«įį¾įį·įŗ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"įį¬įøįįįŗįį¼į®"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"įįįŗįįį±į¬ įį°įį±į¬įįŗįøįį½įįŗįį»į¬įø įįį¾įįį«"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"įįįŗįįį±į¬ įį°įį±į¬įįŗįøįį½įįŗįį»į¬įøįį¾įį·įŗ įįįįŗįįįÆįįŗįį±į¬ įį°įį±į¬įįŗįøįį½įįŗįį»į¬įøįįᯠį¤įį±įį¬įį½įįŗ įį¼įįŗįįį«įįįŗ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"įį°įį±į¬įįŗįøįį½įįŗįįÆį¶įøį įį»įįŗįįÆįįŗįį¼įįŗįø"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"į
įį¬įøįįįÆįįŗįøį”įį
įŗįį»į¬įøįįᯠįįį·įŗįįįŗįį¬įøįį¼įįŗ į”į±į¬įįŗįį¼į±įį±į¬įį·įŗįį½įįŗ įįįŗį¹įį±įįį»į¬įøį”įį¼į
įŗ įį¼įįŗįįį«įįįŗį įįįŗįøįįįÆį·įįᯠįį¼įį·įŗįįįŗįįįÆį·įį« (įįįÆį·) įįįŗįįįŗįįįį½į²įį«į"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"įį°įį±į¬įįŗįøįį½įįŗįįᯠį”įį»įįįŗįįį½į±įø įįįįŗįøįį»įÆįįŗįįįŗ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"įį°įį±į¬įįŗįøįį½įįŗįįÆį¶įøįįįÆįįŗįįį·įŗ į”įįŗįįŗįį¾įį·įŗ į
įį¬įøįįįÆįįŗįøįį»į¬įø į
į®įį¶įįįŗ į¤įį±įį¬įįᯠįįįÆį·įį«"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"įį°įį±į¬įįŗįøįį±į¬įįŗįį¶"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"į
į®įį¶įįįŗ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"įį°įį±į¬įįŗįøįį½įįŗ įįįŗįįįÆįįŗįįįŗį"</string> - <string name="restart_button_description" msgid="6712141648865547958">"įįįÆįį±į¬įįŗįøįį±į¬įį¼įįŗįį½įįŗįøį”įį½įįŗ į¤į”įįŗįįŗįį¼įįŗį
įįįŗ įįįÆį·įįįÆįįŗįįįŗį"</string> + <string name="restart_button_description" msgid="4564728020654658478">"įįįÆįį±į¬įįŗįøįį±į¬įį¼įįŗįį½įįŗįøį”įį½įįŗ į¤į”įįŗįįŗįį¼įįŗį
įįįŗ įįįÆį·įį«"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"įįįŗįįįŗįį»į¬įøįį½įįŗ į¤į”įįŗįįŗįį”įį»įįÆįøį”į
į¬įøįįᯠįį¼į±į¬įįŗįøįįįŗ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"į”įį»įįÆįøį
į¬įø įį¼į±į¬įįŗįøįįįŗ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"įįįŗįįį¬įį¼įæįį¬įį¬įøį\nįį¼įįŗįįįŗįįįŗ įįįÆį·įį«"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"įį±į¬įįŗįøįįį½į¬įøįį°įøįį¬įøį\nįį¼įįŗįį¼į±į¬įįŗįøįįįŗ įįįÆį·įį«"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"įįįŗįįį¬įį¼įæįᬠįįį¾įįį°įøįį¬įøį įįįŗįįįŗ įįįÆį·įį«į"</string> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 6c80144fee18..cd656b846ccf 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytt til Ćøverst til hĆøyre"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytt til nederst til venstre"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytt til nederst til hĆøyre"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"vis <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-innstillinger"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Lukk boblen"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ikke vis samtaler i bobler"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Greit"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen nylige bobler"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nylige bobler og avviste bobler vises her"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat med bobler"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nye samtaler vises som ikoner i et hjĆørne nede pĆ„ skjermen. Trykk for Ć„ Ć„pne dem, eller dra for Ć„ lukke dem."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontroller bobler nĆ„r som helst"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Trykk her for Ć„ administrere hvilke apper og samtaler som kan vises i bobler"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Trykk for Ć„ starte denne appen pĆ„ nytt for bedre visning."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Trykk for Ć„ starte denne appen pĆ„ nytt og fĆ„ en bedre visning"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre hĆøyde/bredde-forholdet for denne appen i innstillingene"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Endre hĆøyde/bredde-forholdet"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for Ć„ tilpasse"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet lĆøst?\nTrykk for Ć„ gĆ„ tilbake"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for Ć„ lukke."</string> diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml index f9f580530f8b..3300bc68c084 100644 --- a/libs/WindowManager/Shell/res/values-ne/strings.xml +++ b/libs/WindowManager/Shell/res/values-ne/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सिरानमा ą¤¦ą¤¾ą¤Æą¤¾ą¤ą¤¤ą¤æą¤° ą¤øą¤¾ą¤°ą„ą¤Øą„ą¤¹ą„ą¤øą„"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ą¤Ŗą„ą¤ą¤¾ą¤°ą¤®ą¤¾ ą¤¬ą¤¾ą¤Æą¤¾ą¤ą¤¤ą¤æą¤° ą¤øą¤¾ą¤°ą„ą¤Øą„ą¤¹ą„ą¤øą„"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ą¤Ŗą„ą¤ą¤¾ą¤°ą¤®ą¤¾ ą¤¦ą¤¾ą¤Æą¤¾ą¤ą¤¤ą¤æą¤° ą¤øą¤¾ą¤°ą„ą¤Øą„ą¤¹ą„ą¤øą„"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¤ą¤ą„ą¤øą„ą¤Ŗą¤¾ą¤Øą„औ ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¤ą„ą¤²ą„ą¤Æą¤¾ą¤Ŗą„स ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ą¤ą¤¾ ą¤øą„ą¤ą¤æą¤ą¤¹ą¤°ą„"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą¤µą¤¾ą¤°ą„ą¤¤ą¤¾ą¤²ą¤¾ą¤Ŗ ą¤¬ą¤¬ą¤²ą¤ą„ ą¤°ą„ą¤Ŗą¤®ą¤¾ ą¤Øą¤¦ą„ą¤ą¤¾ą¤ą¤Æą„सą„"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą¤¬ą„ą¤ą„ą¤"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ą¤¹ą¤¾ą¤²ą„ą¤ą¤¾ ą¤¬ą¤¬ą¤²ą¤¹ą¤°ą„ ą¤ą„ननą„"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ą¤¹ą¤¾ą¤²ą„ą¤ą¤¾ बबल र ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą¤æą¤ą¤ą¤¾ ą¤¬ą¤¬ą¤²ą¤¹ą¤°ą„ ą¤Æą¤¹ą¤¾ą¤ ą¤¦ą„ą¤ą¤æą¤Øą„ ą¤ą¤Øą„"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"बबल ą¤Ŗą„ą¤°ą¤Æą„ą¤ ą¤ą¤°ą„ ą¤ą„ą¤°ą¤¾ą¤ą¤¾ą¤Øą„ ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ą¤Øą¤Æą¤¾ą¤ ą¤µą¤¾ą¤°ą„ą¤¤ą¤¾ą¤²ą¤¾ą¤Ŗą¤¹ą¤°ą„ ą¤¤ą¤Ŗą¤¾ą¤ą¤ą¤ą„ ą¤”ą¤æą¤ą¤¾ą¤ą¤øą¤ą„ ą¤øą„ą¤ą„ą¤°ą¤æą¤Øą¤ą„ ą¤Ŗą„ą¤ą¤¾ą¤°ą¤ą„ ą¤ą„नामा ą¤ą¤ą¤ą¤Øą¤ą¤¾ ą¤°ą„ą¤Ŗą¤®ą¤¾ ą¤¦ą„ą¤ą¤æą¤Øą„ą¤ą¤Øą„ą„¤ ą¤¤ą„ ą¤ą¤ą¤ą¤Ø ą¤ ą„ą¤²ą„ ą¤¬ą¤Øą¤¾ą¤ą¤Ø ą¤ą„याप ą¤ą¤°ą„ą¤Øą„ą¤¹ą„ą¤øą„ ą¤µą¤¾ ą¤¤ą„ ą¤ą¤ą¤ą¤Ø ą¤¹ą¤ą¤¾ą¤ą¤Ø ą¤”ą„ą¤°ą„ą¤Æą¤¾ą¤ ą¤ą¤°ą„ą¤Øą„ą¤¹ą„ą¤øą„ą„¤"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ą¤ą„ą¤Øą¤øą„ą¤ą„ ą¤¬ą„ą¤²ą¤¾ ą¤¬ą¤¬ą¤²ą¤øą¤®ą„ą¤¬ą¤Øą„ą¤§ą„ ą¤øą„ą¤µą¤æą¤§ą¤¾ ą¤Øą¤æą¤Æą¤Øą„ą¤¤ą„रण ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ą¤ą„न ą¤ą¤Ŗ र ą¤ą„ą¤°ą¤¾ą¤ą¤¾ą¤Øą„ बबल ą¤Ŗą„ą¤°ą¤Æą„ą¤ ą¤ą¤°ą„न ą¤øą¤ą„ą¤ą¤Øą„ ą¤ą¤Øą„ą¤Øą„ ą¤ą„ą¤°ą¤¾ą¤ą„ ą¤µą„ą¤Æą¤µą¤øą„ऄापन ą¤ą¤°ą„न ą¤Æą¤¹ą¤¾ą¤ ą¤ą„याप ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ą¤µą„ą¤Æą¤µą¤øą„ऄापन ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल ą¤¹ą¤ą¤¾ą¤ą¤Æą„ą„¤"</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą¤Æą„ ą¤ą¤Ŗ ą¤
ą¤ ą¤°ą¤¾ą¤®ą„ą¤°ą„ ą¤¹ą„ą¤°ą„न ą¤®ą¤æą¤²ą„ą¤Øą„ ą¤¬ą¤Øą¤¾ą¤ą¤Øą¤ą¤¾ ą¤²ą¤¾ą¤ą¤æ ą¤Æą¤øą¤²ą¤¾ą¤ ą¤°ą¤æą¤øą„ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą„न ą¤ą„याप ą¤ą¤°ą„ą¤Øą„ą¤¹ą„ą¤øą„ą„¤"</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą¤
ą¤ ą¤°ą¤¾ą¤®ą„ą¤°ą„ ą¤ą„ą¤Æą„ ą¤Ŗą„ą¤°ą¤¾ą¤Ŗą„त ą¤ą¤°ą„ą¤Øą¤ą¤¾ ą¤²ą¤¾ą¤ą¤æ ą¤Æą„ ą¤ą¤Ŗ ą¤°ą¤æą¤øą„ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą„न ą¤ą„याप ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą¤øą„ą¤ą¤æą¤ą¤®ą¤¾ ą¤ą¤ ą¤Æą„ ą¤ą¤Ŗą¤ą„ ą¤ą¤øą„ą¤Ŗą„ą¤ą„ą¤ ą¤°ą„ą¤øą¤æą¤Æą„ ą¤Ŗą¤°ą¤æą¤µą¤°ą„ą¤¤ą¤Ø ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą¤ą¤øą„ą¤Ŗą„ą¤ą„ą¤ ą¤°ą„ą¤øą¤æą¤Æą„ ą¤Ŗą¤°ą¤æą¤µą¤°ą„ą¤¤ą¤Ø ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ą¤ą„ą¤Æą¤¾ą¤®ą„ą¤°ą¤¾ą¤øą¤®ą„ą¤¬ą¤Øą„ą¤§ą„ ą¤øą¤®ą¤øą„ą¤Æą¤¾ ą¤¦ą„ą¤ą¤æą¤Æą„?\ną¤øą¤®ą¤øą„ą¤Æą¤¾ हल ą¤ą¤°ą„न ą¤ą„याप ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ą¤øą¤®ą¤øą„ą¤Æą¤¾ हल ą¤ą¤ą¤Ø?\ną¤Ŗą¤¹ą¤æą¤²ą„ą¤ą„ ą¤ą¤øą„ą¤¤ą„ ą¤¬ą¤Øą¤¾ą¤ą¤Ø ą¤ą„याप ą¤ą¤°ą„ą¤Øą„ą¤¹ą„सą„"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ą¤ą„ą¤Æą¤¾ą¤®ą„ą¤°ą¤¾ą¤øą¤®ą„ą¤¬ą¤Øą„ą¤§ą„ ą¤ą„ą¤Øą„ ą¤Ŗą¤Øą¤æ ą¤øą¤®ą¤øą„ą¤Æą¤¾ ą¤ą„न? ą¤ą¤¾ą¤°ą„ą¤ ą¤ą¤°ą„न ą¤ą„याप ą¤ą¤°ą„ą¤Øą„ą¤¹ą„ą¤øą„ą„¤"</string> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index 3064cccba7cc..0d174049dc15 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Naar rechtsboven verplaatsen"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Naar linksonder verplaatsen"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> uitvouwen"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> samenvouwen"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels tonen"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen recente bubbels"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels zie je hier"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatten met bubbels"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nieuwe gesprekken verschijnen als iconen in een benedenhoek van je scherm. Tik om ze uit te vouwen of sleep om ze te sluiten."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bubbels beheren wanneer je wilt"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tik hier om te beheren welke apps en gesprekken als bubbel kunnen worden getoond"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Tik om deze app opnieuw op te starten voor een betere weergave."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Tik om deze app opnieuw op te starten voor een betere weergave"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Wijzig de beeldverhouding van deze app in Instellingen"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Beeldverhouding wijzigen"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Cameraproblemen?\nTik om opnieuw passend te maken."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string> diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml index e4c70537c452..916d8638436c 100644 --- a/libs/WindowManager/Shell/res/values-or/strings.xml +++ b/libs/WindowManager/Shell/res/values-or/strings.xml @@ -26,7 +26,7 @@ <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> \"ą¬ą¬¬ą¬æ-ą¬ą¬æą¬¤ą¬°ą-ą¬ą¬¬ą¬æ\"ą¬°ą ą¬
ą¬ą¬æ"</string> <string name="pip_notification_message" msgid="8854051911700302620">"ą¬ą¬¹ą¬æ ą¬¬ąą¬¶ą¬æą¬·ąą¬ąą <xliff:g id="NAME">%s</xliff:g> ą¬¬ąąą¬¬ą¬¹ą¬¾ą¬° ą¬Øą¬ą¬°ą¬æą¬¬ą¬¾ą¬ą ଯଦି ą¬ą¬Ŗą¬£ ą¬ą¬¾ą¬¹ą¬¾ą¬ą¬Øąą¬¤ą¬æ, ą¬øąą¬ą¬æą¬ąą¬ ą¬ąą¬²ą¬æą¬¬ą¬¾ą¬ą ą¬ą¬¾ą¬Ŗąā ą¬ą¬°ą¬Øąą¬¤ą ą¬ą¬¬ą¬ ą¬ą¬¹ą¬¾ ą¬
ଫąā ą¬ą¬°ą¬æą¬¦ą¬æą¬
ą¬Øąą¬¤ąą„¤"</string> <string name="pip_play" msgid="3496151081459417097">"ą¬Ŗąą¬²ą ą¬ą¬°ą¬Øąą¬¤ą"</string> - <string name="pip_pause" msgid="690688849510295232">"ą¬Ŗą¬ąā ą¬ą¬°ą¬Øąą¬¤ą"</string> + <string name="pip_pause" msgid="690688849510295232">"ବିରତ ą¬ą¬°ą¬Øąą¬¤ą"</string> <string name="pip_skip_to_next" msgid="8403429188794867653">"ą¬Ŗą¬°ą¬¬ą¬°ąą¬¤ąą¬¤ąą¬ą ą¬Æą¬¾ą¬ą¬Øąą¬¤ą"</string> <string name="pip_skip_to_prev" msgid="7172158111196394092">"ą¬Ŗąą¬°ąą¬¬ą¬¬ą¬°ąą¬¤ąą¬¤ąą¬ą ą¬ą¬¾ą¬”ą¬¼ą¬Øąą¬¤ą"</string> <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ą¬°ą¬æą¬øą¬¾ą¬ą¬ą ą¬ą¬°ą¬Øąą¬¤ą"</string> @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ą¬ą¬Ŗą¬°-ą¬”ą¬¾ą¬¹ą¬¾ą¬£ą¬ą ନିą¬
ą¬Øąą¬¤ą"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ତଳ ą¬¬ą¬¾ą¬®ą¬ą ନିą¬
ą¬Øąą¬¤ą"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ତଳ ą¬”ą¬¾ą¬¹ą¬¾ą¬£ą¬ą ନିą¬
ą¬Øąą¬¤ą"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¬¬ą¬æą¬øąą¬¤ą¬¾ą¬° ą¬ą¬°ą¬Øąą¬¤ą"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¬øą¬ąą¬ąą¬ą¬æą¬¤ ą¬ą¬°ą¬Øąą¬¤ą"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ą¬øąą¬ą¬æą¬ą¬øą"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ą¬¬ą¬¬ą¬²ą ą¬ą¬¾ą¬°ą¬ ą¬ą¬°ą¬Øąą¬¤ą"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą¬¬ą¬¾ą¬°ąą¬¤ąą¬¤ą¬¾ą¬³ą¬¾ą¬Ŗą¬ą ą¬¬ą¬¬ą¬²ą ą¬ą¬°ą¬Øąą¬¤ą ନାହିą¬"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą¬¬ąą¬ą¬æą¬ą¬²ą¬æ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ą¬¬ą¬°ąą¬¤ąą¬¤ą¬®ą¬¾ą¬Ø ą¬ąą¬£ą¬øą¬æ ą¬¬ą¬¬ą¬²ą ą¬Øą¬¾ą¬¹ą¬æą¬"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ą¬¬ą¬°ąą¬¤ąą¬¤ą¬®ą¬¾ą¬Øą¬° ą¬ą¬¬ą¬ ą¬ą¬¾ą¬°ą¬ ą¬ą¬°ą¬¾ą¬Æą¬¾ą¬ą¬„ିବା ą¬¬ą¬¬ą¬²ą¬ąą¬”ą¬¼ą¬æą¬ ą¬ą¬ ą¬¾ą¬°ą ą¬¦ąą¬ą¬¾ą¬Æą¬æą¬¬"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ą¬¬ą¬¬ą¬²ą¬ąą¬”ą¬¼ą¬æą¬ą ą¬¬ąąą¬¬ą¬¹ą¬¾ą¬° ą¬ą¬°ą¬æ ą¬ą¬¾ą¬ą ą¬ą¬°ą¬Øąą¬¤ą"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ą¬Øąą¬ ą¬¬ą¬¾ą¬°ąą¬¤ąą¬¤ą¬¾ą¬³ą¬¾ą¬Ŗą¬ąą¬”ą¬¼ą¬æą¬ ą¬ą¬Ŗą¬£ą¬ąą¬ ą¬øąą¬ąą¬°ą¬æą¬Øą¬° ą¬ą¬ ą¬Øą¬æą¬®ąą¬Ø ą¬ąą¬£ą¬°ą ą¬ą¬ą¬ą¬Øą¬ąą¬”ą¬¼ą¬æą¬ ą¬ą¬¾ą¬¬ą ą¬¦ąą¬ą¬¾ą¬Æą¬¾ą¬ą„¤ ą¬øąą¬ąą¬”ą¬¼ą¬æą¬ą ą¬¬ą¬æą¬øąą¬¤ą¬¾ą¬° ą¬ą¬°ą¬æą¬¬ą¬¾ ą¬Ŗą¬¾ą¬ą¬ ą¬ą¬¾ą¬Ŗ ą¬ą¬°ą¬Øąą¬¤ą ą¬ą¬æą¬®ąą¬¬ą¬¾ ą¬øąą¬ąą¬”ą¬¼ą¬æą¬ą ą¬ą¬¾ą¬°ą¬ ą¬ą¬°ą¬æą¬¬ą¬¾ ą¬Ŗą¬¾ą¬ą¬ ą¬”ąą¬°ą¬¾ą¬ ą¬ą¬°ą¬Øąą¬¤ąą„¤"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ą¬Æą ą¬ąą¬£ą¬øą¬æ ą¬øą¬®ąą¬°ą ą¬¬ą¬¬ą¬²ą¬ąą¬”ą¬¼ą¬æą¬ ą¬Øą¬æąą¬Øąą¬¤ąą¬°ą¬£ ą¬ą¬°ą¬Øąą¬¤ą"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ą¬ąą¬ą¬ ą¬ą¬Ŗąą¬ø ą¬ ą¬¬ą¬¾ą¬°ąą¬¤ąą¬¤ą¬¾ą¬³ą¬¾ą¬Ŗą¬ąą¬”଼ିଠବବଲ ą¬¹ąą¬ą¬Ŗą¬¾ą¬°ą¬æą¬¬ ତାହା ą¬Ŗą¬°ą¬æą¬ą¬¾ą¬³ą¬Øą¬¾ ą¬ą¬°ą¬æą¬¬ą¬¾ą¬ą ą¬ą¬ ą¬¾ą¬°ą ą¬ą¬¾ą¬Ŗ ą¬ą¬°ą¬Øąą¬¤ą"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲą"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ą¬Ŗą¬°ą¬æą¬ą¬¾ą¬³ą¬Øą¬¾ ą¬ą¬°ą¬Øąą¬¤ą"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ą¬¬ą¬¬ą¬²ą ą¬ą¬¾ą¬°ą¬ ą¬ą¬°ą¬¾ą¬Æą¬¾ą¬ą¬ą¬æą„¤"</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą¬ą¬ ą¬ą¬¹ąą¬°ą¬æ ą¬ą¬² ą¬ąąą ą¬Ŗą¬¾ą¬ą¬ ą¬ą¬¹ą¬æ ą¬ą¬Ŗ ą¬°ą¬æą¬·ąą¬ą¬¾ą¬°ąą¬ ą¬ą¬°ą¬æą¬¬ą¬¾ą¬ą ą¬ą¬¾ą¬Ŗ ą¬ą¬°ą¬Øąą¬¤ąą„¤"</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą¬ą¬ ą¬ą¬¹ąą¬°ą¬æ ą¬ą¬² ą¬ąąą ą¬Ŗą¬¾ą¬ą¬ ą¬ą¬¹ą¬æ ą¬ą¬Ŗ ą¬°ą¬æą¬·ąą¬ą¬¾ą¬°ąą¬ ą¬ą¬°ą¬æą¬¬ą¬¾ą¬ą ą¬ą¬¾ą¬Ŗ ą¬ą¬°ą¬Øąą¬¤ą"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą¬øąą¬ą¬æą¬ą¬øą¬°ą ą¬ą¬¹ą¬æ ą¬ą¬Ŗą¬° ą¬ą¬ą¬”଼ା ą¬ ą¬ą¬ąą¬ą¬¤ą¬¾ą¬° ą¬
ą¬Øąą¬Ŗą¬¾ą¬¤ ą¬Ŗą¬°ą¬æą¬¬ą¬°ąą¬¤ąą¬¤ą¬Ø ą¬ą¬°ą¬Øąą¬¤ą"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą¬ą¬ą¬”଼ା ą¬ ą¬ą¬ąą¬ą¬¤ą¬¾ą¬° ą¬
ą¬Øąą¬Ŗą¬¾ą¬¤ ą¬Ŗą¬°ą¬æą¬¬ą¬°ąą¬¤ąą¬¤ą¬Ø ą¬ą¬°ą¬Øąą¬¤ą"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ą¬ąąą¬¾ą¬®ąą¬°ą¬¾ą¬°ą ą¬øą¬®ą¬øąąą¬¾ ą¬
ą¬ą¬æ?\ną¬Ŗąą¬£ą¬æ ą¬«ą¬æą¬ ą¬ą¬°ą¬æą¬¬ą¬¾ą¬ą ą¬ą¬¾ą¬Ŗ ą¬ą¬°ą¬Øąą¬¤ą"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ą¬ą¬¹ą¬¾ą¬° ସମାଧାନ ą¬¹ąą¬ą¬Øą¬¾ą¬¹ą¬æą¬?\ną¬«ąą¬°ą¬æą¬Æą¬æą¬¬ą¬¾ ą¬Ŗą¬¾ą¬ą¬ ą¬ą¬¾ą¬Ŗ ą¬ą¬°ą¬Øąą¬¤ą"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ą¬ąąą¬¾ą¬®ąą¬°ą¬¾ą¬°ą ą¬ą¬æą¬ą¬æ ą¬øą¬®ą¬øąąą¬¾ ନାହିą¬? ą¬ą¬¾ą¬°ą¬ ą¬ą¬°ą¬æą¬¬ą¬¾ą¬ą ą¬ą¬¾ą¬Ŗ ą¬ą¬°ą¬Øąą¬¤ąą„¤"</string> diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml index d9f7f340a889..5c936e069f9e 100644 --- a/libs/WindowManager/Shell/res/values-pa/strings.xml +++ b/libs/WindowManager/Shell/res/values-pa/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ąØą©±ąØŖąØ° ਵੱਲ ąØøą©±ąØą© ąØ²ąØæąØąØ¾ąØ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ąØ¹ą©ąØ ąØ¾ąØ ąØµą©±ąØ² ąØą©±ąØ¬ą© ąØ²ąØæąØąØ¾ąØ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ąØ¹ą©ąØ ąØ¾ąØ ąØµą©±ąØ² ąØøą©±ąØą© ąØ²ąØæąØąØ¾ąØ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ਦਾ ਵਿਸਤਾਰ ąØąØ°ą©"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ąØØą©ą©° ąØøąØ®ą©ąØą©"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ąØøą©ąØąØæą©°ąØąØ¾ąØ"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ਬਬਲ ąØØą©ą©° ąØąØ¾ąØ°ąØ ąØąØ°ą©"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ąØą©±ąØ²ąØ¬ąØ¾ąØ¤ \'ਤ੠ਬਬਲ ਨਾ ਲਾąØ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ਸਮਠਲਿąØ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ąØą©ąØ ąØ¹ąØ¾ąØ²ą©ąØ ąØ¬ąØ¬ąØ² ąØØąØ¹ą©ąØ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ąØ¹ąØ¾ąØ²ą©ąØ ąØ¬ąØ¬ąØ² ąØ
ąØ¤ą© ąØąØ¾ąØ°ąØ ąØą©ąØ¤ą© ਬਬਲ ąØą©±ąØ„ą© ąØ¦ąØæąØøąØ£ąØą©"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ਬਬਲ ąØ¦ą© ąØµąØ°ąØ¤ą©ąØ ąØąØ° ąØą© ąØą©ąØ ąØąØ°ą©"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ąØØąØµą©ąØąØąØ ąØą©±ąØ²ąØ¾ąØąØ¬ąØ¾ąØ¤ąØ¾ąØ ąØ¤ą©ąØ¹ąØ¾ąØ”ą© ąØøąØą©ąØ°ą©ąØØ ąØ¦ą© ąØ¹ą©ąØ ąØ²ą© ąØą©ąØØą© ąØµąØæą©±ąØ ąØŖą©ąØ°ąØ¤ą©ąØąØ¾ąØ ąØ¦ą© ąØ°ą©ąØŖ ąØµąØæą©±ąØ ąØ¦ąØæąØąØ¦ą©ąØąØ ąØ¹ąØØą„¤ ąØąØØą©ąØ¹ąØ¾ąØ ąØ¦ąØ¾ ਵਿਸਤਾਰ ąØąØ°ąØØ ąØ²ąØ ąØą©ąØŖ ąØąØ°ą© ąØąØ¾ąØ ąØąØØą©ąØ¹ąØ¾ąØ ąØØą©ą©° ąØąØ¾ąØ°ąØ ąØąØ°ąØØ ąØ²ąØ ąØąØøą©ąØą©ą„¤"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ਬਬਲ ąØ¦ą© ąØøą©ąØµąØæąØ§ąØ¾ ąØØą©ą©° ąØąØæąØøą© ąØµą© ąØµą©ąØ²ą© ąØą©°ąØąØ°ą©ąØ² ąØąØ°ą©"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ąØąØ¹ ąØŖą©ąØ°ąØ¬ą©°ąØ§ąØØ ąØąØ°ąØØ ąØ²ąØ ąØą©±ąØ„ą© ąØą©ąØŖ ąØąØ°ą© ąØąØæ ąØąØæąØ¹ą©ą©ąØąØ ąØąØŖąØ¾ąØ ąØ
ąØ¤ą© ąØą©±ąØ²ąØ¾ąØąØ¬ąØ¾ąØ¤ąØ¾ąØ ਬਬਲ ąØ¹ą© ąØøąØąØ¦ą©ąØąØ ąØ¹ąØØ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ąØ¬ą©ąØ²ąØ¬ą©ąØ²ąØ¾"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ąØŖą©ąØ°ąØ¬ą©°ąØ§ąØØ ąØąØ°ą©"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ąØØą©ą©° ąØąØ¾ąØ°ąØ ąØą©ąØ¤ąØ¾ ąØąØæąØą„¤"</string> - <string name="restart_button_description" msgid="6712141648865547958">"ਬਿਹਤਰ ąØ¦ą©ąØ°ąØæąØøąØ¼ ąØ²ąØ ąØąØø ąØąØŖ ąØØą©ą©° ąØ®ą©ą©-ąØøąØ¼ą©ąØ°ą© ąØąØ°ąØØ ąØ²ąØ ąØą©ąØŖ ąØąØ°ą©ą„¤"</string> + <string name="restart_button_description" msgid="4564728020654658478">"ਬਿਹਤਰ ąØ¦ą©ąØ°ąØæąØøąØ¼ ąØµąØ¾ąØøąØ¤ą© ąØąØø ąØąØŖ ąØØą©ą©° ąØ®ą©ą©-ąØøąØ¼ą©ąØ°ą© ąØąØ°ąØØ ąØ²ąØ ąØą©ąØŖ ąØąØ°ą©"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ąØøą©ąØąØæą©°ąØąØ¾ąØ ąØµąØæą©±ąØ ąØąØ¾ ąØą© ąØąØø ąØąØŖ ąØ¦ą© ąØąØąØ¾ąØ° ąØ
ąØØą©ąØŖąØ¾ąØ¤ ąØØą©ą©° ਬਦਲą©"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ąØąØąØ¾ąØ° ąØ
ąØØą©ąØŖąØ¾ąØ¤ ਬਦਲą©"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ąØą© ąØą©ąØ®ąØ°ą© ąØøą©°ąØ¬ą©°ąØ§ą© ąØøąØ®ą©±ąØøąØæąØąØµąØ¾ąØ ąØ¹ąØØ?\nąØ®ą©ą©-ąØ«ąØæą©±ąØ ąØąØ°ąØØ ąØ²ąØ ąØą©ąØŖ ąØąØ°ą©"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ąØą© ąØąØ¹ ąØ ą©ąØ ąØØąØ¹ą©ąØ ąØ¹ą©ąØ?\nਵਾਪਸ ąØąØ¹ą© ąØąØ°ąØØ ąØ²ąØ ąØą©ąØŖ ąØąØ°ą©"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ąØą© ąØą©ąØ®ąØ°ą© ąØøą©°ąØ¬ą©°ąØ§ą© ąØą©ąØ ąØøąØ®ą©±ąØøąØæąØ ąØØąØ¹ą©ąØ ąØ¹ą©? ąØąØ¾ąØ°ąØ ąØąØ°ąØØ ąØ²ąØ ąØą©ąØŖ ąØąØ°ą©ą„¤"</string> diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml index 0699f5df99ab..abdb5f7eb7ac 100644 --- a/libs/WindowManager/Shell/res/values-pl/strings.xml +++ b/libs/WindowManager/Shell/res/values-pl/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"PrzenieÅ wĀ prawy górny róg"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"PrzenieÅ wĀ lewy dolny róg"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"PrzenieÅ w prawy dolny róg"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozwiÅ dymek <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zwiÅ dymek <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ā ustawienia"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zamknij dymek"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nie wyÅwietlaj rozmowy jako dymka"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Brak ostatnich dymków"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tutaj bÄdÄ
pojawiaÄ siÄ ostatnie iĀ odrzucone dymki"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Czatuj, korzystajÄ
c zĀ dymków"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nowe rozmowy pojawiajÄ
siÄ jako ikony wĀ dolnym rogu ekranu. Kliknij, aby je rozwinÄ
Ä, lub przeciÄ
gnij, aby je zamknÄ
Ä."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ZarzÄ
dzaj dymkami, kiedy chcesz"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Kliknij tutaj, aby zarzÄ
dzaÄ wyÅwietlaniem aplikacji iĀ rozmów jako dymków"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ZarzÄ
dzaj"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ZamkniÄto dymek"</string> - <string name="restart_button_description" msgid="6712141648865547958">"Kliknij, aby zrestartowaÄ aplikacjÄ iĀ zyskaÄ lepszÄ
widocznoÅÄ."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Kliknij w celu zrestartowania aplikacji, aby lepiej siÄ wyÅwietlaÅa."</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ZmieÅ proporcje obrazu aplikacji wĀ Ustawieniach"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ZmieÅ proporcje obrazu"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemy zĀ aparatem?\nKliknij, aby dopasowaÄ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa siÄ nie udaÅa?\nKliknij, aby cofnÄ
Ä"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów zĀ aparatem? Kliknij, aby zamknÄ
Ä"</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml index eea9be26f432..bb68b28075bb 100644 --- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"abrir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ConfiguraƧƵes de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balĆ£o"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"NĆ£o criar balƵes de conversa"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ok"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balĆ£o recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balƵes recentes e dispensados aparecerĆ£o aqui"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Converse usando balƵes"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novas conversas aparecem como Ćcones no canto inferior da tela. Toque neles para abrir ou arraste para dispensar."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controle os balƵes a qualquer momento"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toque aqui para gerenciar quais apps e conversas podem aparecer em balƵes"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"BalĆ£o dispensado."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar o app e atualizar a visualização."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude o tamanho da janela deste app nas ConfiguraƧƵes"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a cĆ¢mera?\nToque para ajustar o enquadramento"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema nĆ£o foi corrigido?\nToque para reverter"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"NĆ£o tem problemas com a cĆ¢mera? Toque para dispensar."</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml index ed0cdb61dacf..2c03c543d1dc 100644 --- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml @@ -24,7 +24,7 @@ <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string> <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu de ecrĆ£ no ecrĆ£"</string> <string name="pip_notification_title" msgid="1347104727641353453">"A app <xliff:g id="NAME">%s</xliff:g> estĆ” no modo de ecrĆ£ no ecrĆ£"</string> - <string name="pip_notification_message" msgid="8854051911700302620">"Se nĆ£o pretende que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definiƧƵes e desative-a."</string> + <string name="pip_notification_message" msgid="8854051911700302620">"Se nĆ£o quer que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definiƧƵes e desative-a."</string> <string name="pip_play" msgid="3496151081459417097">"Reproduzir"</string> <string name="pip_pause" msgid="690688849510295232">"Pausar"</string> <string name="pip_skip_to_next" msgid="8403429188794867653">"Mudar para o seguinte"</string> @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover parte superior direita"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover p/ parte infer. esquerda"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover parte inferior direita"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expandir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"reduzir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"DefiniƧƵes de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar balĆ£o"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"NĆ£o apresentar a conversa em balƵes"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balĆ£o recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balƵes recentes e ignorados vĆ£o aparecer aqui."</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Converse no chat atravĆ©s de balƵes"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"As novas conversas aparecem como Ćcones no canto inferior do ecrĆ£. Toque para as expandir ou arraste para as ignorar."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controle os balƵes em qualquer altura"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toque aqui para gerir que apps e conversas podem aparecer em balƵes"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"BalĆ£o"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"BalĆ£o ignorado."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar esta app e ficar com uma melhor visĆ£o."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar esta app e ficar com uma melhor visĆ£o"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Altere o formato desta app nas DefiniƧƵes"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Altere o formato"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a cĆ¢mara?\nToque aqui para reajustar"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"NĆ£o foi corrigido?\nToque para reverter"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a cĆ¢mara? Toque para ignorar."</string> diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml index eea9be26f432..bb68b28075bb 100644 --- a/libs/WindowManager/Shell/res/values-pt/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"abrir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ConfiguraƧƵes de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balĆ£o"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"NĆ£o criar balƵes de conversa"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ok"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balĆ£o recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balƵes recentes e dispensados aparecerĆ£o aqui"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Converse usando balƵes"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novas conversas aparecem como Ćcones no canto inferior da tela. Toque neles para abrir ou arraste para dispensar."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Controle os balƵes a qualquer momento"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Toque aqui para gerenciar quais apps e conversas podem aparecer em balƵes"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"BalĆ£o dispensado."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar o app e atualizar a visualização."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude o tamanho da janela deste app nas ConfiguraƧƵes"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a cĆ¢mera?\nToque para ajustar o enquadramento"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema nĆ£o foi corrigido?\nToque para reverter"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"NĆ£o tem problemas com a cĆ¢mera? Toque para dispensar."</string> diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index 8a64b1686543..579046b3a45f 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"MutÄ Ć®n dreapta sus"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"MutÄ Ć®n stĆ¢nga jos"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"MutÄ Ć®n dreapta jos"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"extinde <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"restrĆ¢nge <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"SetÄri <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ćnchide balonul"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nu afiČa conversaČia Ć®n balon"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nu existÄ baloane recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Baloanele recente Či baloanele respinse vor apÄrea aici"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chat cu baloane"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ConversaČiile noi apar ca pictograme Ć®n colČul de jos al ecranului. Atinge pentru a le extinde sau trage pentru a le Ć®nchide."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ControleazÄ baloanele oricĆ¢nd"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Atinge aici pentru a gestiona aplicaČiile Či conversaČiile care pot apÄrea Ć®n balon"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"GestioneazÄ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Atinge ca sÄ reporneČti aplicaČia pentru o vizualizare mai bunÄ."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Atinge ca sÄ reporneČti aplicaČia pentru o vizualizare mai bunÄ"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"SchimbÄ raportul de dimensiuni al aplicaČiei din SetÄri"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"SchimbÄ raportul de dimensiuni"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ai probleme cu camera foto?\nAtinge pentru a reĆ®ncadra"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ai remediat problema?\nAtinge pentru a reveni"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu ai probleme cu camera foto? Atinge pentru a Ć®nchide."</string> diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml index a7db44dda901..a8a914cbf9fc 100644 --- a/libs/WindowManager/Shell/res/values-ru/strings.xml +++ b/libs/WindowManager/Shell/res/values-ru/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ŠŠµŃенеŃŃŠø в ŠæŃавŃй веŃŃ
ний ŃŠ³Š¾Š»"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ŠŠµŃенеŃŃŠø в Š»ŠµŠ²Ńй нижний ŃŠ³Š¾Š»"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ŠŠµŃенеŃŃŠø в ŠæŃавŃй нижний ŃŠ³Š¾Š»"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Š Š°Š·Š²ŠµŃŠ½ŃŃŃ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Š”Š²ŠµŃŠ½ŃŃŃ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: наŃŃŃŠ¾Š¹ŠŗŠø"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ДкŃŃŃŃ Š²ŃŠæŠ»ŃŠ²Š°ŃŃŠøŠ¹ ŃŠ°Ń"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ŠŠµ ŠæŠ¾ŠŗŠ°Š·ŃŠ²Š°ŃŃ Š²ŃŠæŠ»ŃŠ²Š°ŃŃŠøŠ¹ ŃŠ°Ń Š“Š»Ń ŃŠ°Š·Š³Š¾Š²Š¾Ńа"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ŠŠ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ŠŠµŃ неГавниŃ
Š²ŃŠæŠ»ŃŠ²Š°ŃŃŠøŃ
ŃŠ°Ńов"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ŠŠ“еŃŃ Š±ŃŠ“ŃŃ ŠæŠ¾ŃŠ²Š»ŃŃŃŃŃ Š½ŠµŠ“Š°Š²Š½ŠøŠµ Šø ŃŠŗŃŃŃŃŠµ Š²ŃŠæŠ»ŃŠ²Š°ŃŃŠøŠµ ŃŠ°ŃŃ."</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ŠŃŠæŠ»ŃŠ²Š°ŃŃŠøŠµ ŃŠ°ŃŃ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ŠŠ¾Š²Ńе ŃŠ°ŃŃ ŠæŠ¾ŃŠ²Š»ŃŃŃŃŃ Š² виГе Š·Š½Š°Ńков в нижней ŃŠ°ŃŃŠø ŃŠŗŃана. ŠŠ¾ŃŠ½ŠøŃŠµŃŃ ŠøŃ
, ŃŃŠ¾Š±Ń ŃŠ°Š·Š²ŠµŃнŃŃŃ. ŠŠµŃŠµŃŠ°ŃŠøŃŠµ ŠøŃ
, ŠµŃŠ»Šø Ń
Š¾ŃŠøŃе закŃŃŃŃ."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ŠŃŠæŠ»ŃŠ²Š°ŃŃŠøŠµ ŃŠ°ŃŃ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Š£ŠŗŠ°Š¶ŠøŃŠµ ŠæŃŠøŠ»Š¾Š¶ŠµŠ½ŠøŃ Šø ŃŠ°Š·Š³Š¾Š²Š¾ŃŃ, Š“Š»Ń ŠŗŠ¾ŃŠ¾ŃŃŃ
ŃŠ°Š·ŃŠµŃŠµŠ½Ń Š²ŃŠæŠ»ŃŠ²Š°ŃŃŠøŠµ ŃŠ°ŃŃ."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ŠŃŠæŠ»ŃŠ²Š°ŃŃŠ°Ń ŠæŠ¾Š“ŃŠŗŠ°Š·ŠŗŠ°"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ŠŠ°ŃŃŃŠ¾ŠøŃŃ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ŠŃŠæŠ»ŃŠ²Š°ŃŃŠøŠ¹ ŃŠ°Ń закŃŃŃ."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ŠŠ°Š¶Š¼ŠøŃе, ŃŃŠ¾Š±Ń ŠæŠµŃŠµŠ·Š°ŠæŃŃŃŠøŃŃ ŠæŃŠøŠ»Š¾Š¶ŠµŠ½ŠøŠµ Šø наŃŃŃŠ¾ŠøŃŃ ŃŠ“Š¾Š±Š½ŃŠ¹ Š“Š»Ń ŠæŃŠ¾ŃмоŃŃŠ° виГ"</string> + <string name="restart_button_description" msgid="4564728020654658478">"ŠŠ°Š¶Š¼ŠøŃе, ŃŃŠ¾Š±Ń ŠæŠµŃŠµŠ·Š°ŠæŃŃŃŠøŃŃ ŠæŃŠøŠ»Š¾Š¶ŠµŠ½ŠøŠµ Šø Š¾ŠæŃŠøŠ¼ŠøŠ·ŠøŃоваŃŃ ŃŠ°Š·Š¼ŠµŃ"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŠŠ·Š¼ŠµŠ½ŠøŃŃ ŃŠ¾Š¾ŃŠ½Š¾ŃŠµŠ½ŠøŠµ ŃŃŠ¾Ńон ŠæŃŠøŠ»Š¾Š¶ŠµŠ½ŠøŃ в наŃŃŃŠ¾Š¹ŠŗŠ°Ń
"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ŠŠ·Š¼ŠµŠ½ŠøŃŃ ŃŠ¾Š¾ŃŠ½Š¾ŃŠµŠ½ŠøŠµ ŃŃŠ¾Ńон"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ŠŃŠ¾Š±Š»ŠµŠ¼Ń Ń ŠŗŠ°Š¼ŠµŃŠ¾Š¹?\nŠŠ°Š¶Š¼ŠøŃе, ŃŃŠ¾Š±Ń ŠøŃŠæŃавиŃŃ."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ŠŠµ помогло?\nŠŠ°Š¶Š¼ŠøŃе, ŃŃŠ¾Š±Ń Š¾ŃŠ¼ŠµŠ½ŠøŃŃ ŠøŠ·Š¼ŠµŠ½ŠµŠ½ŠøŃ."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ŠŠµŃ ŠæŃŠ¾Š±Š»ŠµŠ¼ Ń ŠŗŠ°Š¼ŠµŃŠ¾Š¹? ŠŠ°Š¶Š¼ŠøŃе, ŃŃŠ¾Š±Ń закŃŃŃŃ."</string> diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml index 4153ce2bcf1f..968bc2c3b862 100644 --- a/libs/WindowManager/Shell/res/values-si/strings.xml +++ b/libs/WindowManager/Shell/res/values-si/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ą¶ą·ą·
ą¶Æą¶ą·ą¶«ą¶§ ą¶ą·ą¶± ą¶ŗą¶±ą·ą¶±"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ą¶“ą·ą·
ą·ą¶øą¶§ ą¶ą·ą¶± ą¶ŗą¶±ą·ą¶±"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ą¶“ą·ą·
ą¶Æą¶ą·ą¶«ą¶§ ą¶ą·ą¶± ą¶ŗą¶±ą·ą¶±"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą¶Æą·ą¶ ą·ą¶»ą·ą¶±ą·ą¶±"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą·ą¶ą·ą·
ą¶±ą·ą¶±"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ą·ą·ą¶ą·ą·ą¶øą·"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ą¶¶ą·ą¶¶ą·ą¶½ą· ą¶ą·ą¶ ą¶½ą¶±ą·ą¶±"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą·ą¶ą·ą·ą¶Æą¶ŗ ą¶¶ą·ą¶¶ą·ą¶½ą· ą¶±ą·ą¶Æą¶øą¶±ą·ą¶±"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą¶ą·ą¶»ą·ą¶«ą·"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ą¶øą·ą¶ ą¶¶ą·ą¶¶ą·ą¶½ą· ą¶±ą·ą¶"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ą¶øą·ą¶ ą¶¶ą·ą¶¶ą·ą¶½ą· ą·ą· ą¶ą·ą¶ ą¶½ą· ą¶¶ą·ą¶¶ą·ą¶½ą· ą¶øą·ą·ą· ą¶Æą·ą·ą· ą·ą¶±ą· ą¶ą¶"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ą¶¶ą·ą¶¶ą·ą¶½ą· ą¶·ą·ą·ą·ą¶ą¶ŗą·ą¶±ą· ą¶ą¶ą·ą¶¶ą· ą¶ą¶»ą¶±ą·ą¶±"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ą¶±ą· ą·ą¶ą·ą·ą¶Æ ą¶ą¶¶ą· ą¶ą·ą¶»ą¶ŗą·ą·ą· ą¶“ą·ą·
ą¶ą·ą·
ą·ą¶»ą· ą¶±ą·ą¶»ą·ą¶“ą¶ ą¶½ą·ą· ą¶Æą·ą·ą· ą·ą·. ą¶ą·ą· ą¶“ą·ą·
ą·ą¶½ą· ą¶ą·ą¶»ą·ą¶øą¶§ ą¶ą¶§ą·ą¶§ą· ą¶ą¶»ą¶±ą·ą¶± ą·ą· ą¶ą·ą· ą¶ą·ą¶ ą¶Æą·ą¶øą·ą¶øą¶§ ą¶
ą¶Æą·ą¶±ą·ą¶±."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ą¶ą¶±ą·ą¶ø ą·ą·ą¶½ą·ą·ą¶ ą¶¶ą·ą¶¶ą·ą¶½ą· ą¶“ą·ą¶½ą¶±ą¶ŗ ą¶ą¶»ą¶±ą·ą¶±"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ą¶¶ą·ą¶¶ą·ą¶½ą· ą¶ą·
ą·ą·ą¶ą· ą¶ŗą·ą¶Æą·ą¶øą· ą·ą· ą·ą¶ą·ą·ą¶Æ ą¶ą·
ą¶øą¶±ą·ą¶ą¶»ą¶«ą¶ŗ ą¶ą·ą¶»ą·ą¶øą¶§ ą¶øą·ą·ą· ą¶ą¶§ą·ą¶§ą· ą¶ą¶»ą¶±ą·ą¶±"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ą¶¶ą·ą¶¶ą·ą·
ą·"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ą¶ą·
ą¶øą¶±ą· ą¶ą¶»ą¶±ą·ą¶±"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ą¶¶ą·ą¶¶ą·ą¶½ ą¶ą·ą¶ ą¶Æą¶øą· ą¶ą¶."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą·ą¶©ą· ą·ą·ą¶³ ą¶Æą·ą·ą¶±ą¶ą· ą¶½ą¶¶ą· ą¶ą·ą¶±ą·ą¶ø ą·ą¶³ą·ą· ą¶øą·ą¶ø ą¶ŗą·ą¶Æą·ą¶ø ą¶ŗą·
ą· ą¶ą¶»ą¶¹ą·ą¶øą¶§ ą¶ą¶§ą·ą¶§ą· ą¶ą¶»ą¶±ą·ą¶±."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą·ą¶©ą· ą·ą·ą¶³ ą¶Æą·ą·ą¶±ą¶ą· ą·ą¶³ą·ą· ą¶øą·ą¶ø ą¶ŗą·ą¶Æą·ą¶ø ą¶ŗą·
ą· ą¶ą¶»ą¶¹ą·ą¶øą¶§ ą¶ą¶§ą·ą¶§ą· ą¶ą¶»ą¶±ą·ą¶±"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą·ą·ą¶ą·ą·ą¶øą· ą¶ą·ą·
ą¶øą·ą¶ø ą¶ŗą·ą¶Æą·ą¶øą· ą¶Æą¶»ą·ą·ą¶± ą¶
ą¶±ą·ą¶“ą·ą¶ą¶ŗ ą·ą·ą¶±ą·ą· ą¶ą¶»ą¶±ą·ą¶±"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą¶Æą¶»ą·ą·ą¶± ą¶
ą¶±ą·ą¶“ą·ą¶ą¶ŗ ą·ą·ą¶±ą·ą· ą¶ą¶»ą¶±ą·ą¶±"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ą¶ą·ą¶øą¶»ą· ą¶ą·ą¶§ą¶½ą·ą¶Æ?\ną¶ŗą·
ą· ą·ą·ą· ą¶ą·ą¶»ą·ą¶øą¶§ ą¶ą¶§ą·ą¶§ą· ą¶ą¶»ą¶±ą·ą¶±"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ą¶ą¶ŗ ą·ą·ą·ą¶³ą·ą·ą· ą¶±ą·ą¶ą·ą¶Æ?\ną¶“ą·āą¶»ą¶ą·ą·ą¶»ą·ą¶ą¶±ą¶ŗ ą¶ą·ą¶»ą·ą¶øą¶§ ą¶ą¶§ą·ą¶§ą· ą¶ą¶»ą¶±ą·ą¶±"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ą¶ą·ą¶øą¶»ą· ą¶ą·ą¶§ą¶½ą· ą¶±ą·ą¶øą·ą¶ą·ą¶Æ? ą¶ą·ą¶ ą¶Æą·ą¶øą·ą¶øą¶§ ą¶ą¶§ą·ą¶§ą· ą¶ą¶»ą¶±ą·ą¶±"</string> diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml index 4e38943662ea..303d81b17568 100644 --- a/libs/WindowManager/Shell/res/values-sk/strings.xml +++ b/libs/WindowManager/Shell/res/values-sk/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"PresunĆŗÅ„ doprava nahor"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"PresunĆŗÅ„ doľava nadol"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"PresunĆŗÅ„ doprava nadol"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozbaliÅ„ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zbaliÅ„ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavenia aplikĆ”cie <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ZavrieÅ„ bublinu"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"NezobrazovaÅ„ konverzĆ”ciu ako bublinu"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Dobre"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Žiadne nedĆ”vne bubliny"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tu sa budĆŗ zobrazovaÅ„ nedĆ”vne aĀ zavretĆ© bubliny"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Äet pomocou bublĆn"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"NovĆ© konverzĆ”cie sa zobrazujĆŗ ako ikony vĀ dolnom rohu obrazovky. KlepnutĆm ich rozbalĆte aĀ presunutĆm zavriete."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"OvlĆ”dajte bubliny kedykoľvek"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Klepnite tu aĀ spravujte, ktorĆ© aplikĆ”cie aĀ konverzĆ”cie mÓžu ovlĆ”daÅ„ bubliny"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"SpravovaÅ„"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretĆ”."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Ak chcete zlepÅ”iÅ„ zobrazenie, klepnutĆm tĆŗto aplikĆ”ciu reÅ”tartujte."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Ak chcete zlepÅ”iÅ„ zobrazenie, klepnutĆm tĆŗto aplikĆ”ciu reÅ”tartujte"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ZmeniÅ„ pomer strĆ”n tejto aplikĆ”cie vĀ Nastaveniach"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ZmeniÅ„ pomer strĆ”n"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ProblĆ©my sĀ kamerou?\nKlepnutĆm znova upravte."</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"NevyrieÅ”ilo sa to?\nKlepnutĆm sa vrÔńte."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"NemĆ”te problĆ©my sĀ kamerou? KlepnutĆm zatvorĆte."</string> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index b0e67a771653..6178a1a9a489 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premakni zgoraj desno"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premakni spodaj levo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premakni spodaj desno"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"razÅ”iritev oblaÄka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"strnitev oblaÄka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavitve za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Opusti oblaÄek"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Pogovora ne prikaži v oblaÄku"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"V redu"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ni nedavnih oblaÄkov"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tukaj bodo prikazani tako nedavni kot tudi opuÅ”Äeni oblaÄki"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Klepet z oblaÄki"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Novi pogovori so prikazani kot ikone v enem od spodnjih kotov zaslona. Z dotikom pogovore razÅ”irite, z vleÄenjem jih opustite."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Upravljanje oblaÄkov"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Dotaknite se tukaj za upravljanje aplikacij in pogovorov, ki so lahko prikazani v oblaÄkih"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"MehurÄek"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"OblaÄek je bil opuÅ”Äen."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Äe želite boljÅ”i prikaz, se dotaknite za vnoviÄni zagon te aplikacije."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Äe želite boljÅ”i prikaz, se dotaknite za vnoviÄni zagon te aplikacije."</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Razmerje stranic te aplikacije spremenite v nastavitvah."</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Sprememba razmerja stranic"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Težave s fotoaparatom?\nDotaknite se za vnoviÄno prilagoditev"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string> diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml index 29bfb9268f47..afccac292ebe 100644 --- a/libs/WindowManager/Shell/res/values-sq/strings.xml +++ b/libs/WindowManager/Shell/res/values-sq/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"LĆ«viz lart djathtas"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Zhvendos poshtĆ« majtas"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"LĆ«vize poshtĆ« djathtas"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"zgjero <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"palos <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"CilĆ«simet e <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Hiqe flluskĆ«n"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Mos e vendos bisedĆ«n nĆ« flluskĆ«"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"E kuptova"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nuk ka flluska tĆ« fundit"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Flluskat e fundit dhe flluskat e hequra do tĆ« shfaqen kĆ«tu"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bisedo duke pĆ«rdorur flluskat"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Bisedat e reja shfaqen si ikona nĆ« kĆ«ndin e poshtĆ«m tĆ« ekranit tĆ«nd. Trokit pĆ«r t\'i zgjeruar ose zvarrit pĆ«r t\'i hequr ato."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrollo flluskat nĆ« Ƨdo moment"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Trokit kĆ«tu pĆ«r tĆ« menaxhuar aplikacionet e bisedat qĆ« do tĆ« shfaqen nĆ« flluska"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"FlluskĆ«"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Trokit pĆ«r tĆ« rifilluar kĆ«tĆ« aplikacion pĆ«r njĆ« pamje mĆ« tĆ« mirĆ«."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Trokit pĆ«r ta rinisur kĆ«tĆ« aplikacion pĆ«r njĆ« pamje mĆ« tĆ« mirĆ«"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ndrysho raportin e pamjes sĆ« kĆ«tij aplikacioni te \"CilĆ«simet\""</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ndrysho raportin e pamjes"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ka probleme me kamerĆ«n?\nTrokit pĆ«r ta ripĆ«rshtatur"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit pĆ«r ta rikthyer"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerĆ«n? Trokit pĆ«r ta shpĆ«rfillur."</string> @@ -89,7 +97,7 @@ <string name="letterbox_education_got_it" msgid="4057634570866051177">"E kuptova"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Zgjeroje pĆ«r mĆ« shumĆ« informacion."</string> <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Rinis pĆ«r njĆ« pamje mĆ« tĆ« mirĆ«?"</string> - <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Mund tĆ« rinisĆ«sh aplikacionin nĆ« mĆ«nyrĆ« qĆ« tĆ« duket mĆ« mirĆ« nĆ« ekranin tĆ«nd, por mund tĆ« humbĆ«sh progresin ose Ƨdo ndryshim tĆ« paruajtur"</string> + <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Mund ta rinisĆ«sh aplikacionin nĆ« mĆ«nyrĆ« qĆ« tĆ« duket mĆ« mirĆ« nĆ« ekranin tĆ«nd, por mund ta humbasĆ«sh progresin ose Ƨdo ndryshim tĆ« paruajtur"</string> <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Anulo"</string> <string name="letterbox_restart_restart" msgid="8529976234412442973">"Rinis"</string> <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Mos e shfaq pĆ«rsĆ«ri"</string> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index 307efc9d6a2e..0e70a47f3fcb 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ŠŃемеŃŃŠø Š³Š¾ŃŠµ Š“ŠµŃŠ½Š¾"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ŠŃемеŃŃŠø Голе лево"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ŠŃемеŃŃŠø Голе Š“ŠµŃŠ½Š¾"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ŠæŃŠ¾ŃŠøŃŠøŃе Š¾Š±Š»Š°ŃŠøŃ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ŃŠŗŃŠæŠøŃŠµ Š¾Š±Š»Š°ŃŠøŃ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ŠŠ¾Š“ŠµŃŠ°Š²Š°Ńа за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ŠŠ“Š±Š°ŃŠø Š¾Š±Š»Š°ŃŠøŃ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ŠŠµ ŠŗŠ¾ŃŠøŃŃŠø Š¾Š±Š»Š°ŃŠøŃе за ŠŗŠ¾Š½Š²ŠµŃŠ·Š°ŃŠøŃŃ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ŠŠ°Š¶Šø"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ŠŠµŠ¼Š° неГавниŃ
Š¾Š±Š»Š°ŃŠøŃа"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ŠŠ²Š“е ŃŠµ ŠæŃŠøŠŗŠ°Š·ŃŃŃ Š½ŠµŠ“Š°Š²Š½Šø Šø Š¾Š“Š±Š°ŃŠµŠ½Šø Š¾Š±Š»Š°ŃŠøŃŠø"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ŠŠ°ŃкаŃŃŠµ Ń Š¾Š±Š»Š°ŃŠøŃима"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ŠŠ¾Š²Šµ ŠŗŠ¾Š½Š²ŠµŃŠ·Š°ŃŠøŃŠµ ŃŠµ ŠæŠ¾ŃŠ°Š²ŃŃŃŃ ŠŗŠ°Š¾ иконе Ń Š“Š¾ŃŠµŠ¼ ŃŠ³Š»Ń ŠµŠŗŃŠ°Š½Š°. ŠŠ¾Š“ŠøŃŠ½ŠøŃе Га биŃŃŠµ ŠøŃ
ŠæŃŠ¾ŃŠøŃŠøŠ»Šø или ŠæŃевŃŃŠøŃе Га биŃŃŠµ ŠøŃ
Š¾Š“Š±Š°ŃŠøŠ»Šø."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ŠŠ¾Š½ŃŃŠ¾Š»ŠøŃŠøŃŠµ Š¾Š±Š»Š°ŃŠøŃе Ń ŃŠ²Š°ŠŗŠ¾Š¼ ŃŃŠµŠ½ŃŃŠŗŃ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ŠŠ¾Š“ŠøŃŠ½ŠøŃе овГе Šø Š¾Š“ŃŠµŠ“ŠøŃŠµ ŠŗŠ¾ŃŠµ Š°ŠæŠ»ŠøŠŗŠ°ŃŠøŃе Šø ŠŗŠ¾Š½Š²ŠµŃŠ·Š°ŃŠøŃŠµ Š¼Š¾Š³Ń Š“Š° имаŃŃ Š¾Š±Š»Š°ŃŠøŃ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ŠŠ±Š»Š°ŃŠøŃ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Š£ŠæŃŠ°Š²ŃаŃŃŠµ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ŠŠ±Š»Š°ŃŠøŃ ŃŠµ Š¾Š“Š±Š°ŃŠµŠ½."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ŠŠ¾Š“ŠøŃŠ½ŠøŃе Га биŃŃŠµ ŃŠµŃŃŠ°ŃŃŠ¾Š²Š°Š»Šø Š¾Š²Ń Š°ŠæŠ»ŠøŠŗŠ°ŃŠøŃŃ ŃŠ°Š“Šø Š±Š¾ŃŠµŠ³ ŠæŃŠøŠŗŠ°Š·Š°."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ŠŠ¾Š“ŠøŃŠ½ŠøŃе Га биŃŃŠµ ŃŠµŃŃŠ°ŃŃŠ¾Š²Š°Š»Šø Š¾Š²Ń Š°ŠæŠ»ŠøŠŗŠ°ŃŠøŃŃ ŃŠ°Š“Šø Š±Š¾ŃŠµŠ³ ŠæŃŠøŠŗŠ°Š·Š°"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŠŃŠ¾Š¼ŠµŠ½ŠøŃŠµ ŃŠ°Š·Š¼ŠµŃŃ Š¾Š²Šµ Š°ŠæŠ»ŠøŠŗŠ°ŃŠøŃе Ń ŠŠ¾Š“ŠµŃŠ°Š²Š°Ńима"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ŠŃомени ŃŠ°Š·Š¼ŠµŃŃ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ŠŠ¼Š°Ńе ŠæŃоблема ŃŠ° ŠŗŠ°Š¼ŠµŃŠ¾Š¼?\nŠŠ¾Š“ŠøŃŠ½ŠøŃе Га биŃŃŠµ поново ŃŠŗŠ»Š¾ŠæŠøŠ»Šø"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ŠŃоблем Š½ŠøŃе ŃŠµŃен?\nŠŠ¾Š“ŠøŃŠ½ŠøŃе Га биŃŃŠµ Š²ŃŠ°Ńили"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ŠŠµŠ¼Š°Ńе ŠæŃоблема ŃŠ° ŠŗŠ°Š¼ŠµŃŠ¾Š¼? ŠŠ¾Š“ŠøŃŠ½ŠøŃе Га биŃŃŠµ Š¾Š“Š±Š°ŃŠøŠ»Šø."</string> @@ -89,7 +97,7 @@ <string name="letterbox_education_got_it" msgid="4057634570866051177">"ŠŠ°Š¶Šø"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ŠŃŠ¾ŃŠøŃŠøŃŠµ за ŃŠ¾Ń ŠøŠ½ŃŠ¾ŃŠ¼Š°ŃŠøŃа."</string> <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ŠŠµŠ»ŠøŃе ли Га ŃŠµŃŃŠ°ŃŃŃŃŠµŃе ŃŠ°Š“Šø Š±Š¾ŃŠµŠ³ ŠæŃŠøŠŗŠ°Š·Š°?"</string> - <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ŠŠ¾Š¶ŠµŃе Га ŃŠµŃŃŠ°ŃŃŃŃŠµŃе Š°ŠæŠ»ŠøŠŗŠ°ŃŠøŃŃ Š“Š° би изглеГала Š±Š¾Ńе на ŠµŠŗŃанŃ, Ń ŃŠøŠ¼ ŃŃŠ¾ Š¼Š¾Š¶ŠµŃŠµ Га ŠøŠ·Š³ŃбиŃе оно ŃŃŠ¾ ŃŃŠµ ŃŃŠ°Š“или или Š½ŠµŃаŃŃŠ²Š°Š½Šµ ŠæŃŠ¾Š¼ŠµŠ½Šµ, ако ŠøŃ
има"</string> + <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ŠŠ¾Š¶ŠµŃе Га ŃŠµŃŃŠ°ŃŃŃŃŠµŃе Š°ŠæŠ»ŠøŠŗŠ°ŃŠøŃŃ Š“Š° би изглеГала Š±Š¾Ńе на ŠµŠŗŃанŃ, али Š¼Š¾Š¶ŠµŃе Га ŠøŠ·Š³ŃбиŃе Š½Š°ŠæŃеГак или Š½ŠµŃаŃŃŠ²Š°Š½Šµ ŠæŃŠ¾Š¼ŠµŠ½Šµ"</string> <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ŠŃкажи"</string> <string name="letterbox_restart_restart" msgid="8529976234412442973">"РеŃŃŠ°ŃŃŃŃ"</string> <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ŠŠµ ŠæŃŠøŠŗŠ°Š·ŃŃ ŠæŠ¾Š½Š¾Š²Š¾"</string> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index 33652cd74fda..4c22964e125f 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytta hƶgst upp till hƶger"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytta lƤngst ned till vƤnster"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytta lƤngst ned till hƶger"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"utƶka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"komprimera <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"InstƤllningar fƶr <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"StƤng bubbla"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Visa inte konversationen i bubblor"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Inga nya bubblor"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"De senaste bubblorna och ignorerade bubblor visas hƤr"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Chatta med bubblor"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Nya konversationer visas som ikoner nere i hƶrnet pĆ„ skƤrmen. Tryck fƶr att utƶka eller dra fƶr att stƤnga dem."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Styr bubblor nƤr som helst"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Tryck hƤr fƶr att hantera vilka appar och konversationer som fĆ„r visas i bubblor"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Tryck fƶr att starta om appen och fĆ„ en bƤttre vy."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Tryck fƶr att starta om appen och fĆ„ en bƤttre vy"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ćndra appens bildformat i instƤllningarna"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ćndra bildformat"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problem med kameran?\nTryck fƶr att anpassa pĆ„ nytt"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Lƶstes inte problemet?\nTryck fƶr att Ć„terstƤlla"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck fƶr att ignorera."</string> diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml index fe2ad1f1846d..71aeb61538cd 100644 --- a/libs/WindowManager/Shell/res/values-sw/strings.xml +++ b/libs/WindowManager/Shell/res/values-sw/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sogeza juu kulia"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sogeza chini kushoto"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sogeza chini kulia"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"panua <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kunja <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Mipangilio ya <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ondoa kiputo"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Usiweke viputo kwenye mazungumzo"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Nimeelewa"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hakuna viputo vya hivi majuzi"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viputo vya hivi karibuni na vile vilivyoondolewa vitaonekana hapa"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Piga gumzo ukitumia viputo"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Mazungumzo mapya huonekana kama aikoni katika kona ya chini ya skrini yako. Gusa ili uyapanue au buruta ili uyaondoe."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Dhibiti viputo wakati wowote"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Gusa hapa ili udhibiti programu na mazungumzo yanayoweza kutumia viputo"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Gusa ili uzime kisha uwashe programu hii, ili upate mwonekano bora."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Gusa ili uzime kisha uwashe programu hii, ili upate mwonekano bora"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Badilisha uwiano wa programu hii katika Mipangilio"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Badilisha uwiano wa kipengele"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Je, kuna hitilafu za kamera?\nGusa ili urekebishe"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string> diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml index fd5f0e646d9b..aab05daca0ae 100644 --- a/libs/WindowManager/Shell/res/values-ta/strings.xml +++ b/libs/WindowManager/Shell/res/values-ta/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ą®®ąÆą®²ąÆ ą®µą®²ą®ŖąÆą®ŖąÆą®±ą®®ą®¾ą® ą®Øą®ą®°ąÆą®¤ąÆą®¤ąÆ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ą®ąÆą®“ąÆ ą®ą®ą®ŖąÆą®ŖąÆą®±ą®®ą®¾ą® ą®Øą®ą®°ąÆą®¤ąÆą®¤ąÆ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ą®ąÆą®“ąÆ ą®µą®²ą®¤ąÆą®ŖąÆą®±ą®®ą®¾ą® ą®Øą®ą®°ąÆą®¤ąÆą®¤ąÆ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą® ą®µą®æą®°ą®æą®µą®¾ą®ąÆą®ąÆą®®ąÆ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą®ą®ąÆ ą®ąÆą®°ąÆą®ąÆą®ąÆą®®ąÆ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ą®
ą®®ąÆą®ŖąÆą®ŖąÆą®ą®³ąÆ"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ą®ąÆą®®ą®æą®“௠ą®
ą®ą®±ąÆą®±ąÆ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą®ą®°ąÆą®Æą®¾ą®ą®²ąÆą®ąÆ ą®ąÆą®®ą®æą®“ą®¾ą®ąÆą®ą®¾ą®¤ąÆ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą®ą®°ą®æ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ą®ą®®ąÆą®Ŗą®¤ąÆą®¤ą®æą®Æ ą®ąÆą®®ą®æą®“ąÆą®ą®³ąÆ ą®ą®²ąÆą®²ąÆ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ą®ą®®ąÆą®Ŗą®¤ąÆą®¤ą®æą®Æ ą®ąÆą®®ą®æą®“ąÆą®ą®³ąÆą®®ąÆ ą®Øą®æą®°ą®¾ą®ą®°ą®æą®ąÆą®ą®ŖąÆą®Ŗą®ąÆą® ą®ąÆą®®ą®æą®“ąÆą®ą®³ąÆą®®ąÆ ą®ą®ąÆą®ąÆ ą®¤ąÆą®©ąÆą®±ąÆą®®ąÆ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ą®ąÆą®®ą®æą®“ąÆą®ą®³ąÆą®ŖąÆ ą®Ŗą®Æą®©ąÆą®Ŗą®ąÆą®¤ąÆą®¤ą®æ ą®ą®°ąÆą®Æą®¾ą®ąÆą®ąÆą®ą®³ąÆ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ą®ŖąÆą®¤ą®æą®Æ ą®ą®°ąÆą®Æą®¾ą®ą®²ąÆą®ą®³ąÆ ą®ą®ąÆą®ą®³ąÆ ą®¤ą®æą®°ąÆą®Æą®æą®©ąÆ ą®ąÆą®“ąÆ ą®®ąÆą®²ąÆą®Æą®æą®²ąÆ ą®ą®ą®¾ą®©ąÆą®ą®³ą®¾ą®ą®¤ąÆ ą®¤ąÆą®©ąÆą®±ąÆą®®ąÆ. ą®
ą®µą®±ąÆą®±ąÆ ą®µą®æą®°ą®æą®µą®¾ą®ąÆą® ą®¤ą®ąÆą®ą®µąÆą®®ąÆ ą®
ą®²ąÆą®²ą®¤ąÆ ą®Øą®æą®°ą®¾ą®ą®°ą®æą®ąÆą® ą®ą®“ąÆą®ąÆą®ą®µąÆą®®ąÆ."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ą®ą®ŖąÆą®ŖąÆą®¤ąÆ ą®µąÆą®£ąÆą®ąÆą®®ą®¾ą®©ą®¾ą®²ąÆą®®ąÆ ą®ąÆą®®ą®æą®“ąÆą®ą®³ąÆą®ąÆ ą®ą®ąÆą®ąÆą®ŖąÆą®Ŗą®ąÆą®¤ąÆą®¤ąÆą®ąÆą®ą®³ąÆ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ą®ą®ØąÆą®¤ąÆą®ØąÆą®¤ ą®ą®ŖąÆą®øąÆą®®ąÆ ą®ą®°ąÆą®Æą®¾ą®ą®²ąÆą®ą®³ąÆą®®ąÆ ą®ąÆą®®ą®æą®“ą®æą®Æą®¾ą®ą®²ą®¾ą®®ąÆ ą®ą®©ąÆą®Ŗą®¤ąÆ ą®Øą®æą®°ąÆą®µą®ą®æą®ąÆą® ą®ą®ąÆą®ąÆ ą®¤ą®ąÆą®ąÆą®ąÆą®ą®³ąÆ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"பபிளąÆ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ą®Øą®æą®°ąÆą®µą®ą®æ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ą®ąÆą®®ą®æą®“ąÆ ą®Øą®æą®°ą®¾ą®ą®°ą®æą®ąÆą®ą®ŖąÆą®Ŗą®ąÆą®ą®¤ąÆ."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą®ą®ąÆą®ąÆ ą®¤ą®ąÆą®ąÆą®µą®¤ą®©ąÆ ą®®ąÆą®²ą®®ąÆ ą®ą®ØąÆą®¤ ą®ą®ŖąÆą®øąÆ ą®®ąÆą®£ąÆą®ąÆą®®ąÆ ą®¤ąÆą®ą®ąÆą®ą®æ, ą®ą®ŖąÆą®øąÆ ą®ą®¾ą®ąÆą®ą®ŖąÆą®Ŗą®ąÆą®®ąÆ ą®µą®æą®¤ą®¤ąÆą®¤ąÆ ą®ą®©ąÆą®©ąÆą®®ąÆ ą®ą®æą®±ą®ŖąÆą®Ŗą®¾ą®ąÆą®ą®²ą®¾ą®®ąÆ."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą®ą®ąÆą®ąÆ ą®¤ą®ąÆą®ąÆą®µą®¤ą®©ąÆ ą®®ąÆą®²ą®®ąÆ ą®ą®ØąÆą®¤ ą®ą®ŖąÆą®øąÆ ą®®ąÆą®£ąÆą®ąÆą®®ąÆ ą®¤ąÆą®ą®ąÆą®ą®æ, ą®ą®ŖąÆą®øąÆ ą®ą®¾ą®ąÆą®ą®ŖąÆą®Ŗą®ąÆą®®ąÆ ą®µą®æą®¤ą®¤ąÆą®¤ąÆ ą®ą®©ąÆą®©ąÆą®®ąÆ ą®ą®æą®±ą®ŖąÆą®Ŗą®¾ą®ąÆą®ą®²ą®¾ą®®ąÆ"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą®
ą®®ąÆą®ŖąÆą®ŖąÆą®ą®³ą®æą®²ąÆ ą®ą®ØąÆą®¤ ą®ą®ŖąÆą®øą®æą®©ąÆ ą®¤ąÆą®±ąÆą®± ą®µą®æą®ą®æą®¤ą®¤ąÆą®¤ąÆ ą®®ą®¾ą®±ąÆą®±ąÆą®®ąÆ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą®¤ąÆą®±ąÆą®± ą®µą®æą®ą®æą®¤ą®¤ąÆą®¤ąÆ ą®®ą®¾ą®±ąÆą®±ąÆą®®ąÆ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ą®ąÆą®®ą®°ą®¾ ą®¤ąÆą®ą®°ąÆą®Ŗą®¾ą®© ą®ą®æą®ąÆą®ą®²ąÆą®ą®³ą®¾?\ną®®ąÆą®£ąÆą®ąÆą®®ąÆ ą®ŖąÆą®°ąÆą®¤ąÆą®¤ ą®¤ą®ąÆą®ą®µąÆą®®ąÆ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ą®ą®æą®ąÆą®ą®²ąÆą®ą®³ąÆ ą®ą®°ą®æą®ąÆą®ÆąÆą®Æą®ŖąÆą®Ŗą®ą®µą®æą®²ąÆą®²ąÆą®Æą®¾?\ną®®ą®¾ą®±ąÆą®±ą®æą®Æą®®ąÆą®ąÆą® ą®¤ą®ąÆą®ą®µąÆą®®ąÆ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ą®ąÆą®®ą®°ą®¾ ą®¤ąÆą®ą®°ąÆą®Ŗą®¾ą®© ą®ą®æą®ąÆą®ą®²ąÆą®ą®³ąÆ ą®ą®¤ąÆą®µąÆą®®ąÆ ą®ą®²ąÆą®²ąÆą®Æą®¾? ą®Øą®æą®°ą®¾ą®ą®°ą®æą®ąÆą® ą®¤ą®ąÆą®ą®µąÆą®®ąÆ."</string> diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index 6f95aa9c8305..1e407bf255de 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ą°ą°ą±ą°µ ą°ą±ą°”ą°æą°µą±ą°Ŗą±ą°Øą°ą± ą°ą°°ą±ą°Ŗą±"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ą°¦ą°æą°ą±ą°µ ą°ą°”ą°®ą°µą±ą°Ŗą±ą°Øą°ą± ą°¤ą°°ą°²ą°æą°ą°ą±"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ą°¦ą°æą°ą°µą± ą°ą±ą°”ą°æą°µą±ą°Ŗą±ą°Øą°ą± ą°ą°°ą±ą°Ŗą±"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ą°µą°æą°øą±ą°¤ą°°ą°æą°ą°ą°ą°”ą°æ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>āą°Øą± ą°ą±ą°¦ą°æą°ą°ą°ą°”ą°æ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ą°øą±ą°ą±ą°ą°æą°ą°ą±āą°²ą±"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ą°¬ą°¬ą±ą°²ą±āą°Øą± ą°µą°æą°øą±ą°®ą°°ą°æą°ą°ą±"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą°øą°ą°ą°¾ą°·ą°£ą°Øą± ą°¬ą°¬ą±ą°²ą± ą°ą±ą°Æą°µą°¦ą±ą°¦ą±"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ą°
ą°°ą±ą°„ą°®ą±ą°ą°¦ą°æ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ą°ą°ą±ą°µą°²ą°æ ą°¬ą°¬ą±ą°²ą±ą°øą± ą°ą°µą± ą°²ą±ą°µą±"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ą°ą°ą±ą°µą°²ą°æ ą°¬ą°¬ą±ą°²ą±ą°øą± ą°®ą°°ą°æą°Æą± ą°¤ą±ą°øą°æą°µą±ą°øą°æą°Ø ą°¬ą°¬ą±ą°²ą±ą°øą± ą°ą°ą±ą°ą°” ą°ą°Øą°æą°Ŗą°æą°øą±ą°¤ą°¾ą°Æą°æ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ą°¬ą°¬ą±ą°²ą±ą°øą±āą°Øą± ą°ą°Ŗą°Æą±ą°ą°æą°ą°ą°æ ą°ą°¾ą°ą± ą°ą±ą°Æą°ą°”ą°æ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ą°ą±ą°¤ą±ą°¤ ą°øą°ą°ą°¾ą°·ą°£ą°²ą± ą°®ą± ą°øą±ą°ą±ą°°ą±ą°Øą± ą°ą°æą°ą°¦ ą°®ą±ą°²ą°Ø ą°ą°æą°¹ą±ą°Øą°¾ą°²ą±ą°ą°¾ ą°ą°Øą°æą°Ŗą°æą°øą±ą°¤ą°¾ą°Æą°æ. ą°ą±ą°Æą°¾ą°Ŗą± ą°ą±ą°øą°æ ą°µą°¾ą°ą°æą°Øą°æ ą°µą°æą°øą±ą°¤ą°°ą°æą°ą°ą°ą°”ą°æ ą°²ą±ą°¦ą°¾ ą°²ą°¾ą°ą°æ ą°µą°æą°øą±ą°®ą°°ą°æą°ą°ą°ą°”ą°æ."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ą°¬ą°¬ą±ą°²ą±ą°øą±āą°Øą± ą°ą°Ŗą±ą°Ŗą±ą°”ą±ą°Øą°¾ ą°ą°ą°ą±ą°°ą±ą°²ą± ą°ą±ą°Æą°ą°”ą°æ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ఠయాపą±āą°²ą±, ą°øą°ą°ą°¾ą°·ą°£ą°²ą°Øą± ą°¬ą°¬ą±ą°²ą± ą°ą±ą°Æą°¾ą°²ą± ą°®ą±ą°Øą±ą°ą± ą°ą±ą°Æą°”ą°¾ą°Øą°æą°ą°æ ą°ą°ą±ą°ą°” ą°ą±ą°Æą°¾ą°Ŗą± ą°ą±ą°Æą°ą°”ą°æ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ą°¬ą°¬ą±ą°²ą±"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ą°®ą±ą°Øą±ą°ą± ą°ą±ą°Æą°ą°”ą°æ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ą°¬ą°¬ą±ą°²ą± ą°µą°æą°øą±ą°®ą°°ą°æą°ą°ą°¬ą°”ą°æą°ą°¦ą°æ."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą°®ą±ą°°ą±ą°ą±ą°Ø ą°µą±ą°ą±ą°·ą°£ ą°ą±ą°øą° ఠయాపą±āą°Øą± ą°°ą±ą°øą±ą°ą°¾ą°°ą±ą°ą± ą°ą±ą°Æą°”ą°¾ą°Øą°æą°ą°æ ą°ą±ą°Æą°¾ą°Ŗą± ą°ą±ą°Æą°ą°”ą°æ."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą°®ą±ą°°ą±ą°ą±ą°Ø ą°µą±ą°ą±ą°·ą°£ ą°ą±ą°øą° ఠయాపą±āą°Øą± ą°°ą±ą°øą±ą°ą°¾ą°°ą±ą°ą± ą°ą±ą°Æą°”ą°¾ą°Øą°æą°ą°æ ą°ą±ą°Æą°¾ą°Ŗą± ą°ą±ą°Æą°ą°”ą°æ"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą°øą±ą°ą±ą°ą°æą°ą°ą±āą°²ą°²ą± ą° ą°Æą°¾ą°Ŗą± ą°ą°ą°¾ą°° ą°Øą°æą°·ą±ą°Ŗą°¤ą±ą°¤ą°æą°Øą°æ ą°®ą°¾ą°°ą±ą°ą°ą°”ą°æ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą°ą°ą°¾ą°° ą°Øą°æą°·ą±ą°Ŗą°¤ą±ą°¤ą°æą°Øą°æ ą°®ą°¾ą°°ą±ą°ą°ą°”ą°æ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ą°ą±ą°®ą±ą°°ą°¾ ą°øą°®ą°øą±ą°Æą°²ą± ą°ą°Øą±ą°Øą°¾ą°Æą°¾?\ną°°ą±ą°«ą°æą°ą± ą°ą±ą°Æą°”ą°¾ą°Øą°æą°ą°æ ą°ą±ą°Æą°¾ą°Ŗą± ą°ą±ą°Æą°ą°”ą°æ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని ą°øą°®ą°øą±ą°Æą°Øą± ą°Ŗą°°ą°æą°·ą±ą°ą°°ą°æą°ą°ą°²ą±ą°¦ą°¾?\ną°Ŗą±ą°°ą±ą°µą°øą±ą°„ą°æą°¤ą°æą°ą°æ ą°®ą°¾ą°°ą±ą°ą°”ą°¾ą°Øą°æą°ą°æ ą°ą±ą°Æą°¾ą°Ŗą± ą°ą±ą°Æą°ą°”ą°æ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ą°ą±ą°®ą±ą°°ą°¾ ą°øą°®ą°øą±ą°Æą°²ą± ą°²ą±ą°µą°¾? ą°¤ą±ą°øą°æą°µą±ą°Æą°”ą°¾ą°Øą°æą°ą°æ ą°ą±ą°Æą°¾ą°Ŗą± ą°ą±ą°Æą°ą°”ą°æ."</string> diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml index cc0333efd82b..8d2e28b9492f 100644 --- a/libs/WindowManager/Shell/res/values-television/config.xml +++ b/libs/WindowManager/Shell/res/values-television/config.xml @@ -45,11 +45,16 @@ <integer name="config_pipForceCloseDelay">5000</integer> <!-- Animation duration when exit starting window: fade out icon --> - <integer name="starting_window_app_reveal_icon_fade_out_duration">0</integer> + <integer name="starting_window_app_reveal_icon_fade_out_duration">200</integer> - <!-- Animation duration when exit starting window: reveal app --> - <integer name="starting_window_app_reveal_anim_delay">0</integer> + <!-- Animation delay when exit starting window: reveal app --> + <integer name="starting_window_app_reveal_anim_delay">200</integer> <!-- Animation duration when exit starting window: reveal app --> - <integer name="starting_window_app_reveal_anim_duration">0</integer> + <integer name="starting_window_app_reveal_anim_duration">300</integer> + + <!-- Default animation type when hiding the starting window. The possible values are: + - 0 for radial vanish + slide up + - 1 for fade out --> + <integer name="starting_window_exit_animation_type">1</integer> </resources> diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml index 6733940fd445..77a9f4b3ec76 100644 --- a/libs/WindowManager/Shell/res/values-th/strings.xml +++ b/libs/WindowManager/Shell/res/values-th/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ąø¢ą¹ąø²ąø¢ą¹ąøąøą¹ąø²ąøąøąø§ąø²ąøąø"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ąø¢ą¹ąø²ąø¢ą¹ąøąøą¹ąø²ąøąøą¹ąø²ąø¢ąø„ą¹ąø²ąø"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ąø¢ą¹ąø²ąø¢ą¹ąøąøą¹ąø²ąøąøąø²ąø§ąø„ą¹ąø²ąø"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ąøąø¢ąø²ąø¢ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ยุภ<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ąøąø²ąø£ąøąø±ą¹ąøąøą¹ąø² <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ąøąø“ąøąøąø±ąøą¹ąøąø“ąø„"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ą¹ąø”ą¹ąøą¹ąøąøą¹ąøŖąøąøąøąø²ąø£ąøŖąøąøąøąø²ą¹ąøą¹ąøąøąø±ąøą¹ąøąø“ąø„"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ąø£ąø±ąøąøąø£ąø²ąø"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ą¹ąø”ą¹ąø”ąøµąøąø±ąøą¹ąøąø“ąø„ą¹ąø”ąø·ą¹ąøą¹ąø£ą¹ąø§ą¹ ąøąøµą¹"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ąøąø±ąøą¹ąøąø“ąø„ąøąøµą¹ą¹ąøŖąøąøą¹ąø„ąø°ąøąøµą¹ąøąø“ąøą¹ąøą¹ąø”ąø·ą¹ąøą¹ąø£ą¹ąø§ą¹ ąøąøµą¹ąøąø°ąøąø£ąø²ąøąøąøąøµą¹ąøąøµą¹"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ą¹ąøąøą¹ąøąø¢ą¹ąøą¹ąøąø±ąøą¹ąøąø“ąø„"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ąøąø²ąø£ąøŖąøąøąøąø²ąøąø£ąø±ą¹ąøą¹ąø«ąø”ą¹ą¹ ąøąø°ąøąø£ąø²ąøąøą¹ąøą¹ąøą¹ąøąøąøąøąøąøµą¹ąø”ąøøąø”ąø„ą¹ąø²ąøąøąøąøąø«ąøą¹ąø²ąøąø ą¹ąøąø¢ąøŖąø²ąø”ąø²ąø£ąøą¹ąøąø°ą¹ąøąø·ą¹ąøąøąø¢ąø²ąø¢ąø«ąø£ąø·ąøąø„ąø²ąøą¹ąøąø·ą¹ąøąøąø“ąø"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ąøąø§ąøąøąøøąø”ąøąø±ąøą¹ąøąø“ąø„ą¹ąøą¹ąøąøøąøą¹ąø”ąø·ą¹ąø"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ą¹ąøąø°ąøąøµą¹ąøąøµą¹ą¹ąøąø·ą¹ąøąøąø±ąøąøąø²ąø£ą¹ąøąøą¹ąø„ąø°ąøąø²ąø£ąøŖąøąøąøąø²ąøąøµą¹ą¹ąøŖąøąøą¹ąøą¹ąøąøąø±ąøą¹ąøąø“ąø„ą¹ąøą¹"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ąøąø±ąøą¹ąøąø“ąø„"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ąøąø±ąøąøąø²ąø£"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ąøąø“ąøąøąø±ąøą¹ąøąø“ąø„ą¹ąø„ą¹ąø§"</string> - <string name="restart_button_description" msgid="6712141648865547958">"ą¹ąøąø°ą¹ąøąø·ą¹ąøąø£ąøµąøŖąøąø²ąø£ą¹ąøą¹ąøąøąøąøµą¹ą¹ąø„ąø°ąø£ąø±ąøąø”ąøøąø”ąø”ąøąøąøąøµą¹ąøąøµąø¢ąø“ą¹ąøąøąø¶ą¹ąø"</string> + <string name="restart_button_description" msgid="4564728020654658478">"ą¹ąøąø°ą¹ąøąø·ą¹ąøąø£ąøµąøŖąøąø²ąø£ą¹ąøą¹ąøąøąøąøµą¹ą¹ąø„ąø°ąø£ąø±ąøąø”ąøøąø”ąø”ąøąøąøąøµą¹ąøąøµąø¢ąø“ą¹ąøąøąø¶ą¹ąø"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ą¹ąøąø„ąøµą¹ąø¢ąøąøŖąø±ąøąøŖą¹ąø§ąøąø ąø²ąøąøąøąøą¹ąøąøąøąøµą¹ą¹ąøąøąø²ąø£ąøąø±ą¹ąøąøą¹ąø²"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ą¹ąøąø„ąøµą¹ąø¢ąøąøąø±ąøąø£ąø²ąøŖą¹ąø§ąøąøąø§ą¹ąø²ąøąø¢ąø²ąø§"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ąø«ąø²ąøąøąøąøąø±ąøąø«ąø²ąøąø±ąøąøąø„ą¹ąøąø\ną¹ąøąø°ą¹ąøąø·ą¹ąøą¹ąøą¹ą¹ąø"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ąø«ąø²ąøą¹ąø”ą¹ą¹ąøą¹ą¹ąøą¹ą¹ąø\ną¹ąøąø°ą¹ąøąø·ą¹ąøą¹ąøąø„ąøµą¹ąø¢ąøąøąø„ąø±ąø"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ąø«ąø²ąøą¹ąø”ą¹ąøąøąøąø±ąøąø«ąø²ąøąø±ąøąøąø„ą¹ąøąø ą¹ąøąø°ą¹ąøąø·ą¹ąøąøąø“ąø"</string> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index 8cf4eb484378..757da92ef72c 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ilipat sa kanan sa itaas"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ilipat sa kaliwa sa ibaba"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ilipat sa kanan sa ibaba"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"I-expand ang <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"i-collapse ang <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Mga setting ng <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"I-dismiss ang bubble"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Huwag ipakita sa bubble ang mga pag-uusap"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Walang kamakailang bubble"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Lalabas dito ang mga kamakailang bubble at na-dismiss na bubble"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Mag-chat gamit ang mga bubble"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Lalabas ang mga bagong pag-uusap bilang mga icon sa sulok sa ibaba ng iyong screen. I-tap para i-expand ang mga ito o i-drag para i-dismiss ang mga ito."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrolin ang mga bubble anumang oras"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Mag-tap dito para pamahalaan ang mga app at conversion na puwedeng mag-bubble"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string> - <string name="restart_button_description" msgid="6712141648865547958">"I-tap para i-restart ang app na ito para sa mas magandang view."</string> + <string name="restart_button_description" msgid="4564728020654658478">"I-tap para i-restart ang app na ito para sa mas magandang view"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Baguhin ang aspect ratio ng app na ito sa Mga Setting"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Baguhin ang aspect ratio"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"May mga isyu sa camera?\nI-tap para i-refit"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string> diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml index 1454435b3de7..9c4bf8df9308 100644 --- a/libs/WindowManager/Shell/res/values-tr/strings.xml +++ b/libs/WindowManager/Shell/res/values-tr/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"SaÄ Ć¼ste taÅı"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sol alta taÅı"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"SaÄ alta taÅı"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"geniÅlet: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"daralt: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"BaloncuÄu kapat"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"GƶrüÅmeyi baloncuk olarak gƶrüntüleme"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Anladım"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Son kapatılan baloncuk yok"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son baloncuklar ve kapattıÄınız baloncuklar burada gƶrünür"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Baloncukları kullanarak sohbet edin"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Yeni gƶrüÅmeler, ekranınızın alt kƶÅesinde simge olarak gƶrünür. Bunları dokunarak geniÅletebilir veya sürükleyerek kapatabilirsiniz."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Baloncukları istediÄiniz zaman kontrol edin"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Buraya dokunarak baloncuk olarak gƶsterilecek uygulama ve gƶrüÅmeleri yƶnetin"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Yƶnet"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Bu uygulamayı yeniden baÅlatarak daha iyi bir gƶrünüm elde etmek iƧin dokunun."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Bu uygulamayı yeniden baÅlatarak daha iyi bir gƶrünüm elde etmek iƧin dokunun"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Bu uygulamanın en boy oranını Ayarlar\'dan deÄiÅtirin"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"En boy oranını deÄiÅtir"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kameranızda sorun mu var?\nDüzeltmek iƧin dokunun"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu iÅlem sorunu düzeltmedi mi?\nİÅlemi geri almak iƧin dokunun"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak iƧin dokunun."</string> diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml index 78df129d8f50..753fe29ebd41 100644 --- a/libs/WindowManager/Shell/res/values-uk/strings.xml +++ b/libs/WindowManager/Shell/res/values-uk/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ŠŠµŃемŃŃŃŠøŃŠø ŠæŃŠ°Š²Š¾ŃŃŃ ŃŠ³Š¾ŃŃ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ŠŠµŃемŃŃŃŠøŃŠø Š»ŃŠ²Š¾ŃŃŃ ŃŠ½ŠøŠ·"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ŠŠµŃемŃŃŃŠøŃŠø ŠæŃŠ°Š²Š¾ŃŃŃ ŃŠ½ŠøŠ·"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ŃŠ¾Š·Š³Š¾ŃнŃŃŠø \"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>\""</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Š·Š³Š¾ŃŠ½ŃŃŠø \"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>\""</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ŠŠ°Š»Š°ŃŃŃŠ²Š°Š½Š½Ń ŠæŠ°ŃŠ°Š¼ŠµŃŃŠ° \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ŠŠ°ŠŗŃŠøŃŠø ŠæŃŠ“казкŃ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ŠŠµ ŠæŠ¾ŠŗŠ°Š·ŃŠ²Š°ŃŠø ŃŠæŠ»ŠøŠ²Š°ŃŃŃ ŃŠ°ŃŠø Š“Š»Ń ŃŠ¾Š·Š¼Š¾Š²"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ŠŃŠ¾Š·ŃŠ¼Ńло"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ŠŠµŠ¼Š°Ń Š½ŠµŃŠ¾Š“авнŃŃ
ŃŠæŠ»ŠøŠ²Š°ŃŃŠøŃ
ŃŠ°ŃŃŠ²"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Š¢ŃŃ Š·\'ŃŠ²Š»ŃŃŠøŠ¼ŃŃŃŃŃ Š½ŠµŃŠ¾Š“Š°Š²Š½Ń Š¹ Š·Š°ŠŗŃŠøŃŃ ŃŠæŠ»ŠøŠ²Š°ŃŃŃ ŃŠ°ŃŠø"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ДпливаŃŃŠøŠ¹ ŃŠ°Ń"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ŠŠ¾Š²Ń ŃŠ¾Š·Š¼Š¾Š²Šø Š²ŃŠ“Š¾Š±ŃŠ°Š¶Š°ŃŃŃŃŃ Ń Š²ŠøŠ³Š»ŃŠ“Ń Š·Š½Š°ŃŠŗŃв Ń Š½ŠøŠ¶Š½ŃŠ¾Š¼Ń ŠŗŃŃŃ ŠµŠŗŃŠ°Š½Š°. Š¢Š¾ŃŠŗŠ½ŃŃŃŃŃ, ŃŠ¾Š± ŃŠ¾Š·Š³Š¾ŃнŃŃŠø ŃŃ
, або ŠæŠµŃŠµŃŃŠ³Š½ŃŃŃ, ŃŠ¾Š± Š·Š°ŠŗŃŠøŃŠø."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ŠŠ¾Š½ŃŃŠ¾Š»Ń ŃŠæŠ»ŠøŠ²Š°ŃŃŠøŃ
ŃŠ°ŃŃŠ²"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ŠŠ°ŃŠøŃŠ½ŃŃŃ ŃŃŃ, ŃŠ¾Š± Š²ŠøŠ±ŃŠ°ŃŠø, Š“Š»Ń ŃŠŗŠøŃ
Š“Š¾Š“Š°ŃŠŗŃв Ń ŃŠ¾Š·Š¼Š¾Š² Š“Š¾Š·Š²Š¾Š»ŠøŃŠø ŃŠæŠ»ŠøŠ²Š°ŃŃŃ ŃŠ°ŃŠø"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ДпливаŃŃŠµ ŃŠæŠ¾Š²ŃŃŠµŠ½Š½Ń"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ŠŠ°Š»Š°ŃŃŃŠ²Š°ŃŠø"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ДпливаŃŃŠµ ŃŠæŠ¾Š²ŃŃŠµŠ½Š½Ń Š·Š°ŠŗŃŠøŃо."</string> - <string name="restart_button_description" msgid="6712141648865547958">"ŠŠ°ŃŠøŃŠ½ŃŃŃ, ŃŠ¾Š± ŠæŠµŃŠµŠ·Š°ŠæŃŃŃŠøŃŠø ŃŠµŠ¹ Š“Š¾Š“Š°ŃŠ¾Šŗ Š“Š»Ń Š·ŃŃŃŠ½ŃŃŠ¾Š³Š¾ ŠæŠµŃŠµŠ³Š»ŃŠ“Ń."</string> + <string name="restart_button_description" msgid="4564728020654658478">"ŠŠ°ŃŠøŃŠ½ŃŃŃ, ŃŠ¾Š± ŠæŠµŃŠµŠ·Š°ŠæŃŃŃŠøŃŠø ŃŠµŠ¹ Š“Š¾Š“Š°ŃŠ¾Šŗ Š“Š»Ń Š·ŃŃŃŠ½ŃŃŠ¾Š³Š¾ ŠæŠµŃŠµŠ³Š»ŃŠ“Ń"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŠŠ¼ŃŠ½ŠøŃŠø ŃŠ¾ŃŠ¼Š°Ń Š“Š»Ń ŃŃŠ¾Š³Š¾ Š“Š¾Š“Š°ŃŠŗŠ° в налаŃŃŃŠ²Š°Š½Š½ŃŃ
"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ŠŠ¼ŃŠ½ŠøŃŠø ŃŠ¾ŃмаŃ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ŠŃоблеми Š· ŠŗŠ°Š¼ŠµŃŠ¾Ń?\nŠŠ°ŃŠøŃŠ½ŃŃŃ, ŃŠ¾Š± ŠæŃŠøŃŃŠ¾ŃŃŠ²Š°ŃŠø"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ŠŃŠ¾Š±Š»ŠµŠ¼Ń Š½Šµ виŃŃŃŠµŠ½Š¾?\nŠŠ°ŃŠøŃŠ½ŃŃŃ, ŃŠ¾Š± ŃŠŗŠ°ŃŃŠ²Š°ŃŠø Š·Š¼ŃŠ½Šø"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ŠŠµŠ¼Š°Ń ŠæŃŠ¾Š±Š»ŠµŠ¼ ŃŠ· ŠŗŠ°Š¼ŠµŃŠ¾Ń? Š¢Š¾ŃŠŗŠ½ŃŃŃŃŃ, ŃŠ¾Š± Š·Š°ŠŗŃŠøŃŠø."</string> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index ca1642488768..fb0137658560 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ų§ŁŁ¾Ų± ŲÆŲ§Ų¦ŪŚŗ Ų¬Ų§ŁŲØ ŁŪ Ų¬Ų§Ų¦ŁŚŗ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ŁŪŚŪ ŲØŲ§Ų¦ŪŚŗ Ų¬Ų§ŁŲØ ŁŪ Ų¬Ų§Ų¦ŪŚŗ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ŁŪŚŪ ŲÆŲ§Ų¦ŪŚŗ Ų¬Ų§ŁŲØ ŁŪ Ų¬Ų§Ų¦ŪŚŗ"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> Ś©Ł Ł¾Ś¾ŪŁŲ§Ų¦ŪŚŗ"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> Ś©Ł Ų³Ś©ŪŚŪŚŗ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ŲŖŲ±ŲŖŪŲØŲ§ŲŖ"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ŲØŁŲØŁŪ ŲØŲ±Ų®Ų§Ų³ŲŖ Ś©Ų±ŪŚŗ"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ŚÆŁŲŖŚÆŁ ŲØŁŲØŁŪ ŁŪ Ś©Ų±ŪŚŗ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ų³Ł
جھ Ų¢ ŚÆŲ¦Ū"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ś©ŁŲ¦Ū ŲŲ§ŁŪŪ ŲØŁŲØŁŪ ŁŪŪŚŗ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ŲŲ§ŁŪŪ ŲØŁŲØŁŪ Ų§ŁŲ± ŲØŲ±Ų®Ų§Ų³ŲŖ Ų“ŲÆŪ ŲØŁŲØŁŪ ŪŪŲ§Śŗ ŲøŲ§ŪŲ± ŪŁŚŗ ŚÆŪ"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ŲØŁŲØŁŪ Ś©Ū Ų°Ų±ŪŲ¹Ū ŚŪٹ Ś©Ų±ŪŚŗ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ŁŲ¦Ū ŚÆŁŲŖŚÆŁŲ¦ŪŚŗ آپ Ś©Ū Ų§Ų³Ś©Ų±ŪŁ Ś©Ū ŁŪŚŪ Ś©ŁŁŪ Ł
ŪŚŗ Ų¢Ų¦ŪŚ©ŁŲ² Ś©Ū Ų·ŁŲ± پر ŲøŲ§ŪŲ± ŪŁŲŖŪ ŪŪŚŗŪ Ų§ŁŪŪŚŗ Ł¾Ś¾ŪŁŲ§ŁŪ Ś©Ū ŁŪŪ ŲŖŚ¾Ł¾ŲŖŚ¾Ł¾Ų§Ų¦ŪŚŗ ŪŲ§ Ų§ŁŪŪŚŗ ŲØŲ±Ų®Ų§Ų³ŲŖ کرŁŪ Ś©Ū ŁŪŪ ŚÆŚ¾Ų³ŪŁ¹ŪŚŗŪ"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Ś©Ų³Ū ŲØŚ¾Ū ŁŁŲŖ ŲØŁŲØŁŪ Ś©Ł Ś©ŁŁ¹Ų±ŁŁ Ś©Ų±ŪŚŗ"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ŪŪ ŁŲøŁ
کرŁŪ Ś©Ū ŁŪŪ ŪŪŲ§Śŗ ŲŖŚ¾Ł¾ŲŖŚ¾Ł¾Ų§Ų¦ŪŚŗ Ś©Ū Ś©ŁŁ Ų³Ū Ų§ŪŁ¾Ų³ Ų§ŁŲ± ŚÆŁŲŖŚÆŁŲ¦ŪŚŗ ŲØŁŲØŁŪ Ų³Ś©ŲŖŪ ŪŪŚŗ"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ŲØŁŲØŁŪ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ŁŲøŁ
Ś©Ų±ŪŚŗ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ŲØŁŲØŁŪ ŲØŲ±Ų®Ų§Ų³ŲŖ کر ŲÆŪŲ§ ŚÆŪŲ§Ū"</string> - <string name="restart_button_description" msgid="6712141648865547958">"ŲØŪŲŖŲ± Ł
ŁŲøŲ± Ś©Ū ŁŪŪ Ų§Ų³ Ų§ŪŁ¾ Ś©Ł Ų±Ū Ų§Ų³Ł¹Ų§Ų±Ł¹ کرŁŪ Ś©Ū Ų®Ų§Ų·Ų± ŲŖŚ¾Ł¾ŲŖŚ¾Ł¾Ų§Ų¦ŪŚŗŪ"</string> + <string name="restart_button_description" msgid="4564728020654658478">"ŲØŪŲŖŲ± Ł
ŁŲøŲ± Ś©Ū ŁŪŪ Ų§Ų³ Ų§ŪŁ¾ Ś©Ł Ų±Ū Ų§Ų³Ł¹Ų§Ų±Ł¹ کرŁŪ Ś©Ū Ų®Ų§Ų·Ų± ŲŖŚ¾Ł¾ŲŖŚ¾Ł¾Ų§Ų¦ŪŚŗ"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ŲŖŲ±ŲŖŪŲØŲ§ŲŖ Ł
ŪŚŗ Ų§Ų³ Ų§ŪŁ¾ Ś©Ū ŲŖŁŲ§Ų³ŲØŪ Ų“Ų±Ų Ś©Ł ŲŖŲØŲÆŪŁ Ś©Ų±ŪŚŗ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ŲŖŁŲ§Ų³ŲØŪ Ų“Ų±Ų Ś©Ł ŲŖŲØŲÆŪŁ Ś©Ų±ŪŚŗ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ś©ŪŁ
Ų±Ū Ś©Ū Ł
Ų³Ų§Ų¦ŁŲ\nŲÆŁŲØŲ§Ų±Ū ŁŁ¹ کرŁŪ Ś©ŪŁŲ¦Ū ŲŖŚ¾Ł¾ŲŖŚ¾Ł¾Ų§Ų¦ŪŚŗ"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ŪŪ ŲŁ ŁŪŪŚŗ ŪŁŲ§Ų\nŁŁŁ¹Ų§ŁŪ Ś©ŪŁŲ¦Ū ŲŖŚ¾Ł¾ŲŖŚ¾Ł¾Ų§Ų¦ŪŚŗ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ś©ŁŲ¦Ū Ś©ŪŁ
Ų±Ū Ś©Ų§ Ł
Ų³Ų¦ŁŪ ŁŪŪŚŗ ŪŪŲ ŲØŲ±Ų®Ų§Ų³ŲŖ کرŁŪ Ś©ŪŁŲ¦Ū ŲŖŚ¾Ł¾ŲŖŚ¾Ł¾Ų§Ų¦ŪŚŗŪ"</string> diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml index c0dc03366cd6..81e63de9767c 100644 --- a/libs/WindowManager/Shell/res/values-uz/strings.xml +++ b/libs/WindowManager/Shell/res/values-uz/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuqori oŹ»ngga surish"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Quyi chapga surish"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Quyi oŹ»ngga surish"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ni yoyish"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ni yopish"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> sozlamalari"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bulutchani yopish"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Suhbatlar bulutchalar shaklida chiqmasin"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hech qanday bulutcha topilmadi"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Eng oxirgi va yopilgan bulutchali chatlar shu yerda chiqadi"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Bulutchalar yordamida suhbatlashish"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Yangi suhbatlar ekraningizning pastki burchagida belgilar shaklida koŹ»rinadi. Ochish uchun bosing yoki yopish uchun torting"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Bulutchalardagi bildirishnomalar"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Bulutchalarda bildirishnomalar chiqishiga ruxsat beruvchi ilova va suhbatlarni tanlang."</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Yaxshiroq koŹ»rish maqsadida bu ilovani qayta ishga tushirish uchun bosing."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Yaxshiroq koŹ»rish maqsadida bu ilovani qayta ishga tushirish uchun bosing"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Sozlamalar orqali bu ilovaning tomonlar nisbatini oŹ»zgartiring"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tomonlar nisbatini oŹ»zgartirish"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera nosozmi?\nQayta moslash uchun bosing"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string> diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml index 7d974005dd31..16bbd5eda23e 100644 --- a/libs/WindowManager/Shell/res/values-vi/strings.xml +++ b/libs/WindowManager/Shell/res/values-vi/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Chuyį»n lĆŖn trĆŖn cùng bĆŖn phįŗ£i"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Chuyį»n tį»i dʰį»i cùng bĆŖn trĆ”i"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Chuyį»n tį»i dʰį»i cùng bĆŖn phįŗ£i"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"mį» rį»ng <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"thu gį»n <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"CĆ i Äįŗ·t <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Äóng bong bóng"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Dừng sį» dỄng bong bóng cho cuį»c trò chuyį»n"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ÄĆ£ hiį»u"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"KhĆ“ng có bong bóng trò chuyį»n nĆ o gįŗ§n ÄĆ¢y"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Bong bóng trò chuyį»n ÄĆ£ Äóng vĆ bong bóng trò chuyį»n gįŗ§n ÄĆ¢y sįŗ½ xuįŗ„t hiį»n į» ÄĆ¢y"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Trò chuyį»n bįŗ±ng bong bóng trò chuyį»n"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"CĆ”c cuį»c trò chuyį»n mį»i sįŗ½ xuįŗ„t hiį»n dʰį»i dįŗ”ng biį»u tượng į» góc dʰį»i mĆ n hƬnh. HĆ£y nhįŗ„n vĆ o cĆ”c cuį»c trò chuyį»n Äó Äį» mį» rį»ng hoįŗ·c kĆ©o Äį» bį» qua."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kiį»m soĆ”t bong bóng bįŗ„t cứ lĆŗc nĆ o"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Nhįŗ„n vĆ o ÄĆ¢y Äį» quįŗ£n lý viį»c dùng bong bóng cho cĆ”c ứng dỄng vĆ cuį»c trò chuyį»n"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Quįŗ£n lý"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ÄĆ£ Äóng bong bóng."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Nhįŗ„n Äį» khį»i Äį»ng lįŗ”i ứng dỄng nĆ y Äį» xem tį»t hĘ”n."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Nhįŗ„n nĆŗt khį»i Äį»ng lįŗ”i ứng dỄng nĆ y Äį» xem dį»
hĘ”n"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Thay Äį»i tį»· lį» khung hƬnh cį»§a ứng dỄng nĆ y thĆ“ng qua phįŗ§n CĆ i Äįŗ·t"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Thay Äį»i tį»· lį» khung hƬnh"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Có vįŗ„n Äį» vį»i mĆ”y įŗ£nh?\nHĆ£y nhįŗ„n Äį» sį»a lį»i"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bįŗ”n chʰa khįŗÆc phỄc vįŗ„n Äį»?\nHĆ£y nhįŗ„n Äį» hį»§y bį»"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"KhĆ“ng có vįŗ„n Äį» vį»i mĆ”y įŗ£nh? HĆ£y nhįŗ„n Äį» Äóng."</string> diff --git a/libs/WindowManager/Shell/res/values-watch/colors.xml b/libs/WindowManager/Shell/res/values-watch/colors.xml new file mode 100644 index 000000000000..82492bf2af80 --- /dev/null +++ b/libs/WindowManager/Shell/res/values-watch/colors.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources> + <color name="splash_window_background_default">@color/splash_screen_bg_dark</color> +</resources> + diff --git a/libs/WindowManager/Shell/res/values-watch/config.xml b/libs/WindowManager/Shell/res/values-watch/config.xml new file mode 100644 index 000000000000..03736edc4ec6 --- /dev/null +++ b/libs/WindowManager/Shell/res/values-watch/config.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- These resources are around just to allow their values to be customized + for watch products. Do not translate. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- Animation duration when exit starting window: fade out icon --> + <integer name="starting_window_app_reveal_icon_fade_out_duration">50</integer> + + <!-- Animation delay when exit starting window: reveal app --> + <integer name="starting_window_app_reveal_anim_delay">50</integer> + + <!-- Animation duration when exit starting window: reveal app --> + <integer name="starting_window_app_reveal_anim_duration">200</integer> + + <!-- Default animation type when hiding the starting window. The possible values are: + - 0 for radial vanish + slide up + - 1 for fade out --> + <integer name="starting_window_exit_animation_type">1</integer> +</resources> diff --git a/libs/WindowManager/Shell/res/values-watch/dimen.xml b/libs/WindowManager/Shell/res/values-watch/dimen.xml new file mode 100644 index 000000000000..362e72cb4d2d --- /dev/null +++ b/libs/WindowManager/Shell/res/values-watch/dimen.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- The acceptable area ratio of fg icon area/bg icon area, i.e. (48 X 48) / (72 x 72) --> + <item type="dimen" format="float" name="splash_icon_enlarge_foreground_threshold">0.44</item> + <!-- Scaling factor applied to splash icons without provided background i.e. (60 / 48) --> + <item type="dimen" format="float" name="splash_icon_no_background_scale_factor">1.25</item> +</resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml index d1f50dba1ce1..c12ec8423858 100644 --- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ē§»č³å³äøč§"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ē§»č³å·¦äøč§"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ē§»č³å³äøč§"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"å±å¼ā<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ā"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ę¶čµ·ā<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ā"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>设置"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"å
³é对čÆę³”"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"äøä»„对čÆę³”å½¢å¼ę¾ē¤ŗåƹčÆ"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ē„éäŗ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ęčæę²”ę对čÆę³”"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ę¤å¤ä¼ę¾ē¤ŗęčæē对čÆę³”åå·²å
³éē对čÆę³”"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"使ēØåƹčÆę³”č天"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ę°åƹčÆä¼ä»„å¾ę å½¢å¼ę¾ē¤ŗåØå±å¹åŗéØēč§č½äøćē¹ęå¾ę å³åÆå±å¼åƹčÆļ¼ęåØå¾ę å³åÆå
³é对čÆć"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"éę¶ę§å¶åƹčÆę³”"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"ē¹ęę¤å¤å³åÆē®”ēåŖäŗåŗēØå对čÆåÆä»„ę¾ē¤ŗåƹčÆę³”"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ę°ę³”"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ē®”ē"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"å·²å
³é对čÆę³”ć"</string> - <string name="restart_button_description" msgid="6712141648865547958">"ē¹ęå³åÆéåÆę¤åŗēØļ¼č·å¾ę“儽ēč§å¾ä½éŖć"</string> + <string name="restart_button_description" msgid="4564728020654658478">"ē¹ęå³åÆéåÆę¤åŗēØļ¼č·å¾ę“儽ēč§č§ä½éŖ"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"åØā设置āäøę“ę¹ę¤åŗēØē宽é«ęÆ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ę“ę¹é«å®½ęÆ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ēøęŗęé®é¢ļ¼\nē¹ęå³åÆę“äæ®"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ę²”ęč§£å³ę¤é®é¢ļ¼\nē¹ęå³åÆę¢å¤"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ēøęŗę²”ęé®é¢ļ¼ē¹ęå³åÆåæ½ē„ć"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index 6f399e51be4d..c9543488688b 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -24,7 +24,7 @@ <string name="pip_menu_title" msgid="5393619322111827096">"éøå®"</string> <string name="pip_menu_accessibility_title" msgid="8129016817688656249">"ē«äøē«éøå®"</string> <string name="pip_notification_title" msgid="1347104727641353453">"ć<xliff:g id="NAME">%s</xliff:g>ćē®ååØē«äøē«ęØ”å¼"</string> - <string name="pip_notification_message" msgid="8854051911700302620">"å¦ęęØäøę³ć<xliff:g id="NAME">%s</xliff:g>ć使ēØę¤åč½ļ¼č«č¼ę仄éåčØå®ļ¼ē¶å¾åēØę¤åč½ć"</string> + <string name="pip_notification_message" msgid="8854051911700302620">"å¦ęä½ äøę³ć<xliff:g id="NAME">%s</xliff:g>ć使ēØę¤åč½ļ¼č«č¼ę仄éåčØå®ļ¼ē¶å¾åēØę¤åč½ć"</string> <string name="pip_play" msgid="3496151081459417097">"ęę¾"</string> <string name="pip_pause" msgid="690688849510295232">"ę«å"</string> <string name="pip_skip_to_next" msgid="8403429188794867653">"č·³å°äøäøå"</string> @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ē§»å»å³äøč§"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ē§»å»å·¦äøč§"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ē§»å»å³äøč§"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ęé<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ę¶å<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ć<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>ćčØå®"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ééå°č¦ēŖę°£ę³”"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"äøč¦ééå°č¦ēŖé”Æē¤ŗå°č©±"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ē„éäŗ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ę²ęęčæę¾ä½æēØēå°č¦ēŖ"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ęčæä½æēØåééēå°č¦ēŖęåØé裔锯示"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"使ēØå°č©±ę°£ę³”é²č”å³ęéčØ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ē«é¢åŗéØēč§č½ę锯示ę°å°č©±å示ćč¼ęå³åÆå±éå示ļ¼ęę³å³åÆééć"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"éØęę§å¶å°č©±ę°£ę³”"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"č¼ęé裔å³åÆē®”ēåŖäŗęēØēØå¼åå°č©±åÆä»„使ēØå°č©±ę°£ę³”"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"氣泔"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ē®”ē"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"å°č©±ę°£ę³”å·²ééć"</string> - <string name="restart_button_description" msgid="6712141648865547958">"č¼ęäø¦éę°ååę¤ęēØēØå¼ļ¼ä»„åå¾ę“ä½³ēč§ēé«é©ć"</string> + <string name="restart_button_description" msgid="4564728020654658478">"č¼ęäø¦éę°ååę¤ęēØēØå¼ļ¼ä»„åå¾ę“ä½³ēč§ēé«é©"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"åå¾ćčØå®ćč®ę“ę¤ęēØēØå¼ēé·åƬęÆ"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"č®ę“é·åƬęÆ"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ēøę©ęåé”ļ¼\nč¼ęå³åÆäæ®ę£"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ęŖč½äæ®ę£åé”ļ¼\nč¼ęå³åÆéå"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ēøę©ååé”ļ¼ć©äøäøå°±åÆä»„å³åÆéåä½¢ć"</string> @@ -88,8 +96,8 @@ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"åØęēØēØå¼å¤č¼ęå
©äøå³åÆčŖæę“ä½ē½®"</string> <string name="letterbox_education_got_it" msgid="4057634570866051177">"ē„éäŗ"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"å±éå³åÆę„ē詳ę
ć"</string> - <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"č¦éę°ååę¹åęŖ¢č¦ē«é¢åļ¼"</string> - <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ęØåÆéę°ååęēØēØå¼ļ¼č®ē³»ēµ±ę“ę°ęŖ¢č¦ē«é¢ļ¼ä½ē³»ēµ±åÆč½äøęå²åē®åé²åŗ¦åęØä½åŗēä»»ä½č®ę“"</string> + <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"č¦éę°åå仄ę¹åęŖ¢č¦ē«é¢åļ¼"</string> + <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ä½ åÆéę°ååęēØēØå¼ļ¼č®ē³»ēµ±ę“ę°ęŖ¢č¦ē«é¢ļ¼ä½ē³»ēµ±åÆč½äøęå²åē®åé²åŗ¦åä½ ä½åŗēä»»ä½č®ę“"</string> <string name="letterbox_restart_cancel" msgid="1342209132692537805">"åę¶"</string> <string name="letterbox_restart_restart" msgid="8529976234412442973">"éę°åå"</string> <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"äøč¦å锯示"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index 4ca49e167118..d25bfd74ca04 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ē§»č³å³äøę¹"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ē§»č³å·¦äøę¹"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ē§»č³å³äøę¹"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"å±éć<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ć"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ę¶åć<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ć"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ć<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>ćčØå®"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"ééå°č©±ę”"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"äøč¦ä»„å°č©±ę”å½¢å¼é”Æē¤ŗå°č©±"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ęē„éäŗ"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ęčæę²ęä»»ä½å°č©±ę”"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ęčæēå°č©±ę”åå·²ééēå°č©±ę”ę锯示åØé裔"</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"ééå°č©±ę”é²č”å³ęéčØ"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"ē«é¢åŗéØēč§č½ę锯示ę°å°č©±å示ćč¼č§øåÆå±éå示ļ¼ęę³å³åÆééć"</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"ä½ éØęåÆä»„ę§ē®”å°č©±ę”ēåé
čØå®"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"č¼č§øé裔å³åÆē®”ēåŖäŗęēØēØå¼åå°č©±åÆé”Æē¤ŗå°č©±ę”"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"泔泔"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ē®”ē"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"å·²éé泔泔ć"</string> - <string name="restart_button_description" msgid="6712141648865547958">"č«č¼č§øäø¦éę°ååę¤ęēØēØå¼ļ¼åå¾ę“čÆå„½ēč§ēé«é©ć"</string> + <string name="restart_button_description" msgid="4564728020654658478">"č¼č§øę¤ęééę°ååéåęēØēØå¼ļ¼å³åÆē²å¾ę“čÆå„½ēč§ēé«é©"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"åå¾ćčØå®ćč®ę“éåęēØēØå¼ē锯示ęÆä¾"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"č®ę“锯示ęÆä¾"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ēøę©ęåé”åļ¼\nč¼č§øå³åÆäæ®ę£"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ęŖäæ®ę£åé”åļ¼\nč¼č§øå³åÆéå"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ēøę©ę²åé”åļ¼č¼č§øå³åÆééć"</string> diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml index 478b5a62c8a5..bd62b65ccdf2 100644 --- a/libs/WindowManager/Shell/res/values-zu/strings.xml +++ b/libs/WindowManager/Shell/res/values-zu/strings.xml @@ -66,6 +66,8 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Hambisa phezulu ngakwesokudla"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Hambisa inkinobho ngakwesokunxele"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Hambisa inkinobho ngakwesokudla"</string> + <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"nweba <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"goqa <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> izilungiselelo"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Cashisa ibhamuza"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ungayibhamuzi ingxoxo"</string> @@ -76,10 +78,16 @@ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ngiyezwa"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Awekho amabhamuza akamuva"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Amabhamuza akamuva namabhamuza asusiwe azobonakala lapha."</string> + <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Xoxa usebenzisa amabhamuza"</string> + <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Izingxoxo ezintsha zivela njengezithonjana ekhoneni eliphansi lesikrini sakho. Thepha ukuze uzikhulise noma uhudule ukuze uzichithe."</string> + <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Lawula amabhamuza noma nini"</string> + <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Thepha lapha ukuze ulawule ukuthi yimaphi ama-app kanye nezingxoxo ezingenza amabhamuza"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Thepha ukuze uqale kabusha le app ukuze ibonakale kangcono."</string> + <string name="restart_button_description" msgid="4564728020654658478">"Thepha ukuze uqale kabusha le app ukuze ibonakale kangcono"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Shintsha ukubukeka kwesilinganiselo kwe-app kuMasethingi"</string> + <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Shintsha ukubukeka kwesilinganiselo"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Izinkinga zekhamera?\nThepha ukuze uyilinganise kabusha"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string> diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml index b2ec98bc1b15..9bfd1b44dcca 100644 --- a/libs/WindowManager/Shell/res/values/colors.xml +++ b/libs/WindowManager/Shell/res/values/colors.xml @@ -68,9 +68,10 @@ <color name="desktop_mode_caption_maximize_button_dark">#1C1C17</color> <color name="desktop_mode_caption_app_name_light">#EFF1F2</color> <color name="desktop_mode_caption_app_name_dark">#1C1C17</color> - <color name="desktop_mode_caption_menu_text_color">#191C1D</color> - <color name="desktop_mode_caption_menu_buttons_color_inactive">#191C1D</color> - <color name="desktop_mode_caption_menu_buttons_color_active">#00677E</color> <color name="desktop_mode_resize_veil_light">#EFF1F2</color> <color name="desktop_mode_resize_veil_dark">#1C1C17</color> + <color name="desktop_mode_maximize_menu_button">#DDDACD</color> + <color name="desktop_mode_maximize_menu_button_outline">#797869</color> + <color name="desktop_mode_maximize_menu_button_outline_on_hover">#606219</color> + <color name="desktop_mode_maximize_menu_button_on_hover">#E7E790</color> </resources> diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index a3916b71592b..74364001b6c5 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -58,6 +58,9 @@ if a custom action is present before closing it. --> <integer name="config_pipForceCloseDelay">1000</integer> + <!-- Allow PIP to resize via pinch gesture. --> + <bool name="config_pipEnablePinchResize">true</bool> + <!-- Animation duration when using long press on recents to dock --> <integer name="long_press_dock_anim_duration">250</integer> @@ -83,6 +86,11 @@ <!-- Animation duration when exit starting window: reveal app --> <integer name="starting_window_app_reveal_anim_duration">266</integer> + <!-- Default animation type when hiding the starting window. The possible values are: + - 0 for radial vanish + slide up + - 1 for fade out --> + <integer name="starting_window_exit_animation_type">0</integer> + <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. These values are in DPs and will be converted to pixel sizes internally. --> <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets"> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 99526de56e4e..1f6f7aeadd45 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -143,7 +143,9 @@ <dimen name="bubble_expanded_view_padding">16dp</dimen> <!-- Padding for the edge of the expanded view that is closest to the edge of the screen used when displaying in landscape on a large screen. --> - <dimen name="bubble_expanded_view_largescreen_landscape_padding">128dp</dimen> + <dimen name="bubble_expanded_view_largescreen_landscape_padding">102dp</dimen> + <!-- The width of the expanded view on large screens. --> + <dimen name="bubble_expanded_view_largescreen_width">540dp</dimen> <!-- This should be at least the size of bubble_expanded_view_padding; it is used to include a slight touch slop around the expanded view. --> <dimen name="bubble_expanded_view_slop">8dp</dimen> @@ -401,6 +403,30 @@ <!-- Height of button (32dp) + 2 * margin (5dp each). --> <dimen name="freeform_decor_caption_height">42dp</dimen> + <!-- Height of desktop mode caption for freeform tasks. --> + <dimen name="desktop_mode_freeform_decor_caption_height">42dp</dimen> + + <!-- Height of desktop mode caption for fullscreen tasks. --> + <dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen> + + <!-- The width of the maximize menu in desktop mode. --> + <dimen name="desktop_mode_maximize_menu_width">287dp</dimen> + + <!-- The height of the maximize menu in desktop mode. --> + <dimen name="desktop_mode_maximize_menu_height">112dp</dimen> + + <!-- The larger of the two corner radii of the maximize menu buttons. --> + <dimen name="desktop_mode_maximize_menu_buttons_large_corner_radius">4dp</dimen> + + <!-- The smaller of the two corner radii of the maximize menu buttons. --> + <dimen name="desktop_mode_maximize_menu_buttons_small_corner_radius">2dp</dimen> + + <!-- The corner radius of the maximize menu. --> + <dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen> + + <!-- The radius of the Maximize menu shadow. --> + <dimen name="desktop_mode_maximize_menu_shadow_radius">8dp</dimen> + <!-- The width of the handle menu in desktop mode. --> <dimen name="desktop_mode_handle_menu_width">216dp</dimen> @@ -411,7 +437,10 @@ <dimen name="desktop_mode_handle_menu_windowing_pill_height">52dp</dimen> <!-- The height of the handle menu's "More Actions" pill in desktop mode. --> - <dimen name="desktop_mode_handle_menu_more_actions_pill_height">156dp</dimen> + <dimen name="desktop_mode_handle_menu_more_actions_pill_height">52dp</dimen> + + <!-- The height of the handle menu in desktop mode. --> + <dimen name="desktop_mode_handle_menu_height">328dp</dimen> <!-- The top margin of the handle menu in desktop mode. --> <dimen name="desktop_mode_handle_menu_margin_top">4dp</dimen> @@ -439,4 +468,9 @@ <!-- The height of the area at the top of the screen where a freeform task will transition to fullscreen if dragged until the top bound of the task is within the area. --> <dimen name="desktop_mode_transition_area_height">16dp</dimen> + + <!-- The acceptable area ratio of fg icon area/bg icon area, i.e. (72 x 72) / (108 x 108) --> + <item type="dimen" format="float" name="splash_icon_enlarge_foreground_threshold">0.44</item> + <!-- Scaling factor applied to splash icons without provided background i.e. (192 / 160) --> + <item type="dimen" format="float" name="splash_icon_no_background_scale_factor">1.2</item> </resources> diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml index 8831b610f44a..bc59a235517d 100644 --- a/libs/WindowManager/Shell/res/values/ids.xml +++ b/libs/WindowManager/Shell/res/values/ids.xml @@ -42,4 +42,6 @@ <item type="id" name="action_move_top_right"/> <item type="id" name="action_move_bottom_left"/> <item type="id" name="action_move_bottom_right"/> + + <item type="id" name="dismiss_view"/> </resources> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index 00c63d70d3a0..b556150e2ab9 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -142,6 +142,10 @@ <string name="bubble_accessibility_action_move_bottom_left">Move bottom left</string> <!-- Action in accessibility menu to move the stack of bubbles to the bottom right of the screen. [CHAR LIMIT=30]--> <string name="bubble_accessibility_action_move_bottom_right">Move bottom right</string> + <!-- Accessibility announcement when the stack of bubbles expands. [CHAR LIMIT=NONE]--> + <string name="bubble_accessibility_announce_expand">expand <xliff:g id="bubble_title" example="Messages">%1$s</xliff:g></string> + <!-- Accessibility announcement when the stack of bubbles collapses. [CHAR LIMIT=NONE]--> + <string name="bubble_accessibility_announce_collapse">collapse <xliff:g id="bubble_title" example="Messages">%1$s</xliff:g></string> <!-- Label for the button that takes the user to the notification settings for the given app. --> <string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string> <!-- Text used for the bubble dismiss area. Bubbles dragged to, or flung towards, this area will go away. [CHAR LIMIT=30] --> @@ -163,6 +167,10 @@ <!-- [CHAR LIMIT=NONE] Empty overflow subtitle --> <string name="bubble_overflow_empty_subtitle">Recent bubbles and dismissed bubbles will appear here</string> + <!-- Title text for the bubble bar feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]--> + <string name="bubble_bar_education_stack_title">Chat using bubbles</string> + <!-- Descriptive text for the bubble bar feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=NONE] --> + <string name="bubble_bar_education_stack_text">New conversations appear as icons in a bottom corner of your screen. Tap to expand them or drag to dismiss them.</string> <!-- Title text for the bubble bar "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=60]--> <string name="bubble_bar_education_manage_title">Control bubbles anytime</string> <!-- Descriptive text for the bubble bar "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=80]--> diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml index d902fd49ba60..468cfd5260cc 100644 --- a/libs/WindowManager/Shell/res/values/styles.xml +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -14,7 +14,8 @@ limitations under the License. --> -<resources xmlns:android="http://schemas.android.com/apk/res/android"> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> <!-- Theme used for the activity that shows when the system forced an app to be resizable --> <style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar"> <item name="android:windowBackground">@drawable/forced_resizable_background</item> @@ -37,7 +38,7 @@ <item name="android:padding">16dp</item> <item name="android:textSize">14sp</item> <item name="android:textFontWeight">500</item> - <item name="android:textColor">@color/desktop_mode_caption_menu_text_color</item> + <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item> <item name="android:drawablePadding">16dp</item> <item name="android:background">?android:selectableItemBackground</item> </style> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java index 34bf9e0dd98f..2e5448a9e8d5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java @@ -18,6 +18,7 @@ package com.android.wm.shell; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; +import android.annotation.SuppressLint; import android.app.WindowConfiguration; import android.util.SparseArray; import android.view.SurfaceControl; @@ -29,6 +30,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.sysui.ShellInit; import java.io.PrintWriter; import java.util.List; @@ -44,9 +46,14 @@ public class RootDisplayAreaOrganizer extends DisplayAreaOrganizer { /** Display area leashes, which is mapped by display IDs. */ private final SparseArray<SurfaceControl> mLeashes = new SparseArray<>(); - public RootDisplayAreaOrganizer(Executor executor) { + public RootDisplayAreaOrganizer(@NonNull Executor executor, @NonNull ShellInit shellInit) { super(executor); - List<DisplayAreaAppearedInfo> infos = registerOrganizer(FEATURE_ROOT); + shellInit.addInitCallback(this::onInit, this); + } + + @SuppressLint("MissingPermission") // Only called by SysUI. + private void onInit() { + final List<DisplayAreaAppearedInfo> infos = registerOrganizer(FEATURE_ROOT); for (int i = infos.size() - 1; i >= 0; --i) { onDisplayAreaAppeared(infos.get(i).getDisplayAreaInfo(), infos.get(i).getLeash()); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java index 410ae78dba1b..ab61a48a715c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java @@ -18,6 +18,7 @@ package com.android.wm.shell; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; +import android.annotation.SuppressLint; import android.annotation.UiContext; import android.app.ResourcesManager; import android.content.Context; @@ -32,11 +33,13 @@ import android.view.SurfaceControl; import android.window.DisplayAreaAppearedInfo; import android.window.DisplayAreaInfo; import android.window.DisplayAreaOrganizer; +import android.window.SystemPerformanceHinter; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.sysui.ShellInit; import java.io.PrintWriter; import java.util.ArrayList; @@ -58,12 +61,27 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { /** {@link DisplayAreaContext} list, which is mapped by display IDs. */ private final SparseArray<DisplayAreaContext> mDisplayAreaContexts = new SparseArray<>(); + private final SystemPerformanceHinter.DisplayRootProvider mPerfRootProvider = + new SystemPerformanceHinter.DisplayRootProvider() { + @Override + public SurfaceControl getRootForDisplay(int displayId) { + return mLeashes.get(displayId); + } + }; + private final Context mContext; - public RootTaskDisplayAreaOrganizer(Executor executor, Context context) { + public RootTaskDisplayAreaOrganizer(@NonNull Executor executor, @NonNull Context context, + @NonNull ShellInit shellInit) { super(executor); mContext = context; - List<DisplayAreaAppearedInfo> infos = registerOrganizer(FEATURE_DEFAULT_TASK_CONTAINER); + shellInit.addInitCallback(this::onInit, this); + } + + @SuppressLint("MissingPermission") // Only called by SysUI. + private void onInit() { + final List<DisplayAreaAppearedInfo> infos = + registerOrganizer(FEATURE_DEFAULT_TASK_CONTAINER); for (int i = infos.size() - 1; i >= 0; --i) { onDisplayAreaAppeared(infos.get(i).getDisplayAreaInfo(), infos.get(i).getLeash()); } @@ -229,6 +247,11 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { return mDisplayAreaContexts.get(displayId); } + @NonNull + public SystemPerformanceHinter.DisplayRootProvider getPerformanceRootProvider() { + return mPerfRootProvider; + } + public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java index 06ce37148eaf..8cf869b175ef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java @@ -87,33 +87,28 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle mTransitions.addHandler(this); } - @Override - public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - boolean containsEmbeddingSplit = false; - boolean containsNonEmbeddedChange = false; - final List<TransitionInfo.Change> changes = info.getChanges(); - for (int i = changes.size() - 1; i >= 0; i--) { - final TransitionInfo.Change change = changes.get(i); - if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) { - containsNonEmbeddedChange = true; - } else if (!change.hasFlags(FLAG_FILLS_TASK)) { + /** Whether ActivityEmbeddingController should animate this transition. */ + public boolean shouldAnimate(@NonNull TransitionInfo info) { + boolean containsEmbeddingChange = false; + for (TransitionInfo.Change change : info.getChanges()) { + if (!change.hasFlags(FLAG_FILLS_TASK) && change.hasFlags( + FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) { // Whether the Task contains any ActivityEmbedding split before or after the // transition. - containsEmbeddingSplit = true; + containsEmbeddingChange = true; } } - if (!containsEmbeddingSplit) { + if (!containsEmbeddingChange) { // Let the system to play the default animation if there is no ActivityEmbedding split // window. This allows to play the app customized animation when there is no embedding, // such as the device is in a folded state. return false; } - if (containsNonEmbeddedChange && !handleNonEmbeddedChanges(changes)) { + + if (containsNonEmbeddedChange(info) && !handleNonEmbeddedChanges(info.getChanges())) { return false; } + final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); if (options != null // Scene-transition will be handled by app side. @@ -123,6 +118,17 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle return false; } + return true; + } + + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + + if (!shouldAnimate(info)) return false; + // Start ActivityEmbedding animation. mTransitionCallbacks.put(transition, finishCallback); mAnimationRunner.startAnimation(transition, info, startTransaction, finishTransaction); @@ -136,6 +142,16 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle mAnimationRunner.cancelAnimationFromMerge(); } + /** Whether TransitionInfo contains non-ActivityEmbedding embedded window. */ + private boolean containsNonEmbeddedChange(@NonNull TransitionInfo info) { + for (TransitionInfo.Change change : info.getChanges()) { + if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) { + return true; + } + } + return false; + } + private boolean handleNonEmbeddedChanges(List<TransitionInfo.Change> changes) { final Rect nonClosingEmbeddedArea = new Rect(); for (int i = changes.size() - 1; i >= 0; i--) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java index 9bf3b80d262e..42dc19ce838a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java @@ -52,12 +52,13 @@ public class BackAnimationBackground { /** * Ensures the back animation background color layer is present. + * * @param startRect The start bounds of the closing target. * @param color The background color. * @param transaction The animation transaction. */ - void ensureBackground(Rect startRect, int color, - @NonNull SurfaceControl.Transaction transaction) { + public void ensureBackground( + Rect startRect, int color, @NonNull SurfaceControl.Transaction transaction) { if (mBackgroundSurface != null) { return; } @@ -81,7 +82,12 @@ public class BackAnimationBackground { mIsRequestingStatusBarAppearance = false; } - void removeBackground(@NonNull SurfaceControl.Transaction transaction) { + /** + * Remove the back animation background. + * + * @param transaction The animation transaction. + */ + public void removeBackground(@NonNull SurfaceControl.Transaction transaction) { if (mBackgroundSurface == null) { return; } @@ -93,11 +99,21 @@ public class BackAnimationBackground { mIsRequestingStatusBarAppearance = false; } + /** + * Attach a {@link StatusBarCustomizer} instance to allow status bar animate with back progress. + * + * @param customizer The {@link StatusBarCustomizer} to be used. + */ void setStatusBarCustomizer(StatusBarCustomizer customizer) { mCustomizer = customizer; } - void onBackProgressed(float progress) { + /** + * Update back animation background with for the progress. + * + * @param progress Progress value from {@link android.window.BackProgressAnimator} + */ + public void onBackProgressed(float progress) { if (mCustomizer == null || mStartBounds.isEmpty()) { return; } 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 bb543f24a8ea..3790f04b56eb 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 @@ -25,6 +25,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.content.ContentResolver; @@ -43,7 +44,6 @@ import android.provider.Settings.Global; import android.util.DisplayMetrics; import android.util.Log; import android.util.MathUtils; -import android.util.SparseArray; import android.view.IRemoteAnimationRunner; import android.view.InputDevice; import android.view.KeyCharacterMap; @@ -70,6 +70,7 @@ import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; + import java.util.concurrent.atomic.AtomicBoolean; /** @@ -113,7 +114,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean mShouldStartOnNextMoveEvent = false; /** @see #setTriggerBack(boolean) */ private boolean mTriggerBack; - private FlingAnimationUtils mFlingAnimationUtils; + + private final FlingAnimationUtils mFlingAnimationUtils; + + /** Registry for the back animations */ + private final ShellBackAnimationRegistry mShellBackAnimationRegistry; @Nullable private BackNavigationInfo mBackNavigationInfo; @@ -135,13 +140,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private final TouchTracker mTouchTracker = new TouchTracker(); - private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>(); @Nullable private IOnBackInvokedCallback mActiveCallback; - private CrossActivityAnimation mDefaultActivityAnimation; - private CustomizeActivityAnimation mCustomizeActivityAnimation; - @VisibleForTesting final RemoteCallback mNavigationObserver = new RemoteCallback( new RemoteCallback.OnResultListener() { @@ -169,10 +170,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @NonNull @ShellMainThread ShellExecutor shellExecutor, @NonNull @ShellBackgroundThread Handler backgroundHandler, Context context, - @NonNull BackAnimationBackground backAnimationBackground) { - this(shellInit, shellController, shellExecutor, backgroundHandler, - ActivityTaskManager.getService(), context, context.getContentResolver(), - backAnimationBackground); + @NonNull BackAnimationBackground backAnimationBackground, + ShellBackAnimationRegistry shellBackAnimationRegistry) { + this( + shellInit, + shellController, + shellExecutor, + backgroundHandler, + ActivityTaskManager.getService(), + context, + context.getContentResolver(), + backAnimationBackground, + shellBackAnimationRegistry); } @VisibleForTesting @@ -182,8 +191,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @NonNull @ShellMainThread ShellExecutor shellExecutor, @NonNull @ShellBackgroundThread Handler bgHandler, @NonNull IActivityTaskManager activityTaskManager, - Context context, ContentResolver contentResolver, - @NonNull BackAnimationBackground backAnimationBackground) { + Context context, + ContentResolver contentResolver, + @NonNull BackAnimationBackground backAnimationBackground, + ShellBackAnimationRegistry shellBackAnimationRegistry) { mShellController = shellController; mShellExecutor = shellExecutor; mActivityTaskManager = activityTaskManager; @@ -197,11 +208,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS) .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) .build(); - } - - @VisibleForTesting - void setEnableUAnimation(boolean enable) { - IS_U_ANIMATION_ENABLED = enable; + mShellBackAnimationRegistry = shellBackAnimationRegistry; } private void onInit() { @@ -209,26 +216,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont createAdapter(); mShellController.addExternalInterface(KEY_EXTRA_SHELL_BACK_ANIMATION, this::createExternalInterface, this); - - initBackAnimationRunners(); - } - - private void initBackAnimationRunners() { - if (!IS_U_ANIMATION_ENABLED) { - return; - } - - final CrossTaskBackAnimation crossTaskAnimation = - new CrossTaskBackAnimation(mContext, mAnimationBackground); - mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_TASK, - crossTaskAnimation.mBackAnimationRunner); - mDefaultActivityAnimation = - new CrossActivityAnimation(mContext, mAnimationBackground); - mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_ACTIVITY, - mDefaultActivityAnimation.mBackAnimationRunner); - mCustomizeActivityAnimation = - new CustomizeActivityAnimation(mContext, mAnimationBackground); - // TODO (236760237): register dialog close animation when it's completed. } private void setupAnimationDeveloperSettingsObserver( @@ -359,11 +346,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont void registerAnimation(@BackNavigationInfo.BackTargetType int type, @NonNull BackAnimationRunner runner) { - mAnimationDefinition.set(type, runner); + mShellBackAnimationRegistry.registerAnimation(type, runner); } void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) { - mAnimationDefinition.remove(type); + mShellBackAnimationRegistry.unregisterAnimation(type); } /** @@ -434,9 +421,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont final int backType = backNavigationInfo.getType(); final boolean shouldDispatchToAnimator = shouldDispatchToAnimator(); if (shouldDispatchToAnimator) { - if (mAnimationDefinition.contains(backType)) { - mAnimationDefinition.get(backType).startGesture(); - } else { + if (!mShellBackAnimationRegistry.startGesture(backType)) { mActiveCallback = null; } } else { @@ -459,6 +444,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont sendBackEvent(KeyEvent.ACTION_UP); } + @SuppressLint("MissingPermission") private void sendBackEvent(int action) { final long when = SystemClock.uptimeMillis(); final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK, 0 /* repeat */, @@ -671,21 +657,17 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } final int backType = mBackNavigationInfo.getType(); - final BackAnimationRunner runner = mAnimationDefinition.get(backType); // Simply trigger and finish back navigation when no animator defined. - if (!shouldDispatchToAnimator() || runner == null) { + if (!shouldDispatchToAnimator() + || mShellBackAnimationRegistry.isAnimationCancelledOrNull(backType)) { invokeOrCancelBack(); return; - } - if (runner.isWaitingAnimation()) { + } else if (mShellBackAnimationRegistry.isWaitingAnimation(backType)) { ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Gesture released, but animation didn't ready."); // Supposed it is in post commit animation state, and start the timeout to watch // if the animation is ready. mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION); return; - } else if (runner.isAnimationCancelled()) { - invokeOrCancelBack(); - return; } startPostCommitAnimation(); } @@ -737,12 +719,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mShouldStartOnNextMoveEvent = false; mTouchTracker.reset(); mActiveCallback = null; - // reset to default - if (mDefaultActivityAnimation != null - && mAnimationDefinition.contains(BackNavigationInfo.TYPE_CROSS_ACTIVITY)) { - mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_ACTIVITY, - mDefaultActivityAnimation.mBackAnimationRunner); - } + mShellBackAnimationRegistry.resetDefaultCrossActivity(); if (mBackNavigationInfo != null) { mBackNavigationInfo.onBackNavigationFinished(mTriggerBack); mBackNavigationInfo = null; @@ -750,86 +727,88 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mTriggerBack = false; } - private BackAnimationRunner getAnimationRunnerAndInit() { - int type = mBackNavigationInfo.getType(); - // Initiate customized cross-activity animation, or fall back to cross activity animation - if (type == BackNavigationInfo.TYPE_CROSS_ACTIVITY && mAnimationDefinition.contains(type)) { - final BackNavigationInfo.CustomAnimationInfo animationInfo = - mBackNavigationInfo.getCustomAnimationInfo(); - if (animationInfo != null && mCustomizeActivityAnimation != null - && mCustomizeActivityAnimation.prepareNextAnimation(animationInfo)) { - mAnimationDefinition.get(type).resetWaitingAnimation(); - mAnimationDefinition.set(BackNavigationInfo.TYPE_CROSS_ACTIVITY, - mCustomizeActivityAnimation.mBackAnimationRunner); - } - } - return mAnimationDefinition.get(type); - } private void createAdapter() { - IBackAnimationRunner runner = new IBackAnimationRunner.Stub() { - @Override - public void onAnimationStart(RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, - IBackAnimationFinishedCallback finishedCallback) { - mShellExecutor.execute(() -> { - if (mBackNavigationInfo == null) { - Log.e(TAG, "Lack of navigation info to start animation."); - return; + IBackAnimationRunner runner = + new IBackAnimationRunner.Stub() { + @Override + public void onAnimationStart( + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + IBackAnimationFinishedCallback finishedCallback) { + mShellExecutor.execute( + () -> { + if (mBackNavigationInfo == null) { + Log.e(TAG, "Lack of navigation info to start animation."); + return; + } + final BackAnimationRunner runner = + mShellBackAnimationRegistry.getAnimationRunnerAndInit( + mBackNavigationInfo); + if (runner == null) { + if (finishedCallback != null) { + try { + finishedCallback.onAnimationFinished(false); + } catch (RemoteException e) { + Log.w( + TAG, + "Failed call IBackNaviAnimationController", + e); + } + } + return; + } + mActiveCallback = runner.getCallback(); + mBackAnimationFinishedCallback = finishedCallback; + + ProtoLog.d( + WM_SHELL_BACK_PREVIEW, + "BackAnimationController: startAnimation()"); + runner.startAnimation( + apps, + wallpapers, + nonApps, + () -> + mShellExecutor.execute( + BackAnimationController.this + ::onBackAnimationFinished)); + + if (apps.length >= 1) { + dispatchOnBackStarted( + mActiveCallback, + mTouchTracker.createStartEvent(apps[0])); + } + + // Dispatch the first progress after animation start for + // smoothing the initial animation, instead of waiting for next + // onMove. + final BackMotionEvent backFinish = + mTouchTracker.createProgressEvent(); + dispatchOnBackProgressed(mActiveCallback, backFinish); + if (!mBackGestureStarted) { + // if the down -> up gesture happened before animation + // start, we have to trigger the uninterruptible transition + // to finish the back animation. + startPostCommitAnimation(); + } + }); } - final int type = mBackNavigationInfo.getType(); - final BackAnimationRunner runner = getAnimationRunnerAndInit(); - if (runner == null) { - Log.e(TAG, "Animation didn't be defined for type " - + BackNavigationInfo.typeToString(type)); - if (finishedCallback != null) { - try { - finishedCallback.onAnimationFinished(false); - } catch (RemoteException e) { - Log.w(TAG, "Failed call IBackNaviAnimationController", e); - } - } - return; - } - mActiveCallback = runner.getCallback(); - mBackAnimationFinishedCallback = finishedCallback; - ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startAnimation()"); - runner.startAnimation(apps, wallpapers, nonApps, () -> mShellExecutor.execute( - BackAnimationController.this::onBackAnimationFinished)); - - if (apps.length >= 1) { - dispatchOnBackStarted( - mActiveCallback, mTouchTracker.createStartEvent(apps[0])); + @Override + public void onAnimationCancelled() { + mShellExecutor.execute( + () -> { + if (!mShellBackAnimationRegistry.cancel( + mBackNavigationInfo.getType())) { + return; + } + if (!mBackGestureStarted) { + invokeOrCancelBack(); + } + }); } - - // Dispatch the first progress after animation start for smoothing the initial - // animation, instead of waiting for next onMove. - final BackMotionEvent backFinish = mTouchTracker.createProgressEvent(); - dispatchOnBackProgressed(mActiveCallback, backFinish); - if (!mBackGestureStarted) { - // if the down -> up gesture happened before animation start, we have to - // trigger the uninterruptible transition to finish the back animation. - startPostCommitAnimation(); - } - }); - } - - @Override - public void onAnimationCancelled() { - mShellExecutor.execute(() -> { - final BackAnimationRunner runner = mAnimationDefinition.get( - mBackNavigationInfo.getType()); - if (runner == null) { - return; - } - runner.cancelAnimation(); - if (!mBackGestureStarted) { - invokeOrCancelBack(); - } - }); - } - }; + }; mBackAnimationAdapter = new BackAnimationAdapter(runner); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java index 913239f74bf2..431df212f099 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java @@ -32,7 +32,7 @@ import android.window.IOnBackInvokedCallback; * before it received IBackAnimationRunner#onAnimationStart, so the controller could continue * trigger the real back behavior. */ -class BackAnimationRunner { +public class BackAnimationRunner { private static final String TAG = "ShellBackPreview"; private final IOnBackInvokedCallback mCallback; @@ -44,8 +44,8 @@ class BackAnimationRunner { /** True when the back animation is cancelled */ private boolean mAnimationCancelled; - BackAnimationRunner(@NonNull IOnBackInvokedCallback callback, - @NonNull IRemoteAnimationRunner runner) { + public BackAnimationRunner( + @NonNull IOnBackInvokedCallback callback, @NonNull IRemoteAnimationRunner runner) { mCallback = callback; mRunner = runner; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java index 74a243d34642..114486e848f0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java @@ -51,9 +51,11 @@ import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.annotations.ShellMainThread; +import javax.inject.Inject; + /** Class that defines cross-activity animation. */ @ShellMainThread -class CrossActivityAnimation { +public class CrossActivityAnimation extends ShellBackAnimation { /** * Minimum scale of the entering/closing window. */ @@ -106,6 +108,7 @@ class CrossActivityAnimation { private final SpringAnimation mLeavingProgressSpring; // Max window x-shift in pixels. private final float mWindowXShift; + private final BackAnimationRunner mBackAnimationRunner; private float mEnteringProgress = 0f; private float mLeavingProgress = 0f; @@ -126,11 +129,11 @@ class CrossActivityAnimation { private IRemoteAnimationFinishedCallback mFinishCallback; private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator(); - final BackAnimationRunner mBackAnimationRunner; private final BackAnimationBackground mBackground; - CrossActivityAnimation(Context context, BackAnimationBackground background) { + @Inject + public CrossActivityAnimation(Context context, BackAnimationBackground background) { mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); mBackAnimationRunner = new BackAnimationRunner(new Callback(), new Runner()); mBackground = background; @@ -357,6 +360,11 @@ class CrossActivityAnimation { mTransaction.apply(); } + @Override + public BackAnimationRunner getRunner() { + return mBackAnimationRunner; + } + private final class Callback extends IOnBackInvokedCallback.Default { @Override public void onBackStarted(BackMotionEvent backEvent) { 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 a7dd27a0784f..209d8533ee7d 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 @@ -47,21 +47,23 @@ import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.annotations.ShellMainThread; +import javax.inject.Inject; + /** * Controls the animation of swiping back and returning to another task. * - * This is a two part animation. The first part is an animation that tracks gesture location to - * scale and move the closing and entering app windows. - * Once the gesture is committed, the second part remains the closing window in place. - * The entering window plays the rest of app opening transition to enter full screen. + * <p>This is a two part animation. The first part is an animation that tracks gesture location to + * scale and move the closing and entering app windows. Once the gesture is committed, the second + * part remains the closing window in place. The entering window plays the rest of app opening + * transition to enter full screen. * - * This animation is used only for apps that enable back dispatching via - * {@link android.window.OnBackInvokedDispatcher}. The controller registers - * an {@link IOnBackInvokedCallback} with WM Shell and receives back dispatches when a back - * navigation to launcher starts. + * <p>This animation is used only for apps that enable back dispatching via {@link + * android.window.OnBackInvokedDispatcher}. The controller registers an {@link + * IOnBackInvokedCallback} with WM Shell and receives back dispatches when a back navigation to + * launcher starts. */ @ShellMainThread -class CrossTaskBackAnimation { +public class CrossTaskBackAnimation extends ShellBackAnimation { private static final int BACKGROUNDCOLOR = 0x43433A; /** @@ -104,28 +106,41 @@ class CrossTaskBackAnimation { private final float[] mTmpFloat9 = new float[9]; private final float[] mTmpTranslate = {0, 0, 0}; - + private final BackAnimationRunner mBackAnimationRunner; + private final BackAnimationBackground mBackground; private RemoteAnimationTarget mEnteringTarget; private RemoteAnimationTarget mClosingTarget; private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); - private boolean mBackInProgress = false; - private boolean mIsRightEdge; private float mProgress = 0; private PointF mTouchPos = new PointF(); private IRemoteAnimationFinishedCallback mFinishCallback; private BackProgressAnimator mProgressAnimator = new BackProgressAnimator(); - final BackAnimationRunner mBackAnimationRunner; - private final BackAnimationBackground mBackground; - - CrossTaskBackAnimation(Context context, BackAnimationBackground background) { + @Inject + public CrossTaskBackAnimation(Context context, BackAnimationBackground background) { mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); mBackAnimationRunner = new BackAnimationRunner(new Callback(), new Runner()); mBackground = background; } + private static void computeScaleTransformMatrix(float scale, float[] matrix) { + matrix[0] = scale; + matrix[1] = 0; + matrix[2] = 0; + matrix[3] = 0; + matrix[4] = scale; + matrix[5] = 0; + matrix[6] = 0; + matrix[7] = 0; + matrix[8] = scale; + } + + private static float mapRange(float value, float min, float max) { + return min + (value * (max - min)); + } + private float getInterpolatedProgress(float backProgress) { return 1 - (1 - backProgress) * (1 - backProgress) * (1 - backProgress); } @@ -233,18 +248,6 @@ class CrossTaskBackAnimation { mTransaction.setColorTransform(leash, mTmpFloat9, mTmpTranslate); } - static void computeScaleTransformMatrix(float scale, float[] matrix) { - matrix[0] = scale; - matrix[1] = 0; - matrix[2] = 0; - matrix[3] = 0; - matrix[4] = scale; - matrix[5] = 0; - matrix[6] = 0; - matrix[7] = 0; - matrix[8] = scale; - } - private void finishAnimation() { if (mEnteringTarget != null) { mEnteringTarget.leash.release(); @@ -314,11 +317,12 @@ class CrossTaskBackAnimation { valueAnimator.start(); } - private static float mapRange(float value, float min, float max) { - return min + (value * (max - min)); + @Override + public BackAnimationRunner getRunner() { + return mBackAnimationRunner; } - private final class Callback extends IOnBackInvokedCallback.Default { + private final class Callback extends IOnBackInvokedCallback.Default { @Override public void onBackStarted(BackMotionEvent backEvent) { mProgressAnimator.onBackStarted(backEvent, @@ -340,7 +344,7 @@ class CrossTaskBackAnimation { mProgressAnimator.reset(); onGestureCommitted(); } - }; + } private final class Runner extends IRemoteAnimationRunner.Default { @Override @@ -360,5 +364,5 @@ class CrossTaskBackAnimation { startBackAnimation(); mFinishCallback = finishedCallback; } - }; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java index 2d6ec7547187..aca638c1a5cf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java @@ -55,13 +55,13 @@ import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.annotations.ShellMainThread; -/** - * Class that handle customized close activity transition animation. - */ +import javax.inject.Inject; + +/** Class that handle customized close activity transition animation. */ @ShellMainThread -class CustomizeActivityAnimation { +public class CustomizeActivityAnimation extends ShellBackAnimation { private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator(); - final BackAnimationRunner mBackAnimationRunner; + private final BackAnimationRunner mBackAnimationRunner; private final float mCornerRadius; private final SurfaceControl.Transaction mTransaction; private final BackAnimationBackground mBackground; @@ -88,7 +88,8 @@ class CustomizeActivityAnimation { private final Choreographer mChoreographer; - CustomizeActivityAnimation(Context context, BackAnimationBackground background) { + @Inject + public CustomizeActivityAnimation(Context context, BackAnimationBackground background) { this(context, background, new SurfaceControl.Transaction(), null); } @@ -258,10 +259,12 @@ class CustomizeActivityAnimation { valueAnimator.start(); } - /** - * Load customize animation before animation start. - */ - boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) { + /** Load customize animation before animation start. */ + @Override + public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) { + if (animationInfo == null) { + return false; + } final AnimationLoadResult result = mCustomAnimationLoader.loadAll(animationInfo); if (result != null) { mCloseAnimation = result.mCloseAnimation; @@ -272,6 +275,11 @@ class CustomizeActivityAnimation { return false; } + @Override + public BackAnimationRunner getRunner() { + return mBackAnimationRunner; + } + private final class Callback extends IOnBackInvokedCallback.Default { @Override public void onBackStarted(BackMotionEvent backEvent) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java new file mode 100644 index 000000000000..312e88db863e --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimation.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.back; + +import android.window.BackNavigationInfo; + +import javax.inject.Qualifier; + +/** Base class for all back animations. */ +public abstract class ShellBackAnimation { + @Qualifier + public @interface CrossActivity {} + + @Qualifier + public @interface CrossTask {} + + @Qualifier + public @interface CustomizeActivity {} + + @Qualifier + public @interface ReturnToHome {} + + /** Retrieve the {@link BackAnimationRunner} associated with this animation. */ + public abstract BackAnimationRunner getRunner(); + + /** + * Prepare the next animation with customized animation. + * + * @return true if this type of back animation should override the default. + */ + public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) { + return false; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java new file mode 100644 index 000000000000..62b18f342995 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.back; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.Log; +import android.util.SparseArray; +import android.window.BackNavigationInfo; + +/** Registry for all types of default back animations */ +public class ShellBackAnimationRegistry { + private static final String TAG = "ShellBackPreview"; + + private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>(); + private final ShellBackAnimation mDefaultCrossActivityAnimation; + private final ShellBackAnimation mCustomizeActivityAnimation; + + public ShellBackAnimationRegistry( + @ShellBackAnimation.CrossActivity @Nullable ShellBackAnimation crossActivityAnimation, + @ShellBackAnimation.CrossTask @Nullable ShellBackAnimation crossTaskAnimation, + @ShellBackAnimation.CustomizeActivity @Nullable + ShellBackAnimation customizeActivityAnimation, + @ShellBackAnimation.ReturnToHome @Nullable + ShellBackAnimation defaultBackToHomeAnimation) { + if (crossActivityAnimation != null) { + mAnimationDefinition.set( + BackNavigationInfo.TYPE_CROSS_TASK, crossTaskAnimation.getRunner()); + } + if (crossActivityAnimation != null) { + mAnimationDefinition.set( + BackNavigationInfo.TYPE_CROSS_ACTIVITY, crossActivityAnimation.getRunner()); + } + if (defaultBackToHomeAnimation != null) { + mAnimationDefinition.set( + BackNavigationInfo.TYPE_RETURN_TO_HOME, defaultBackToHomeAnimation.getRunner()); + } + + mDefaultCrossActivityAnimation = crossActivityAnimation; + mCustomizeActivityAnimation = customizeActivityAnimation; + + // TODO(b/236760237): register dialog close animation when it's completed. + } + + void registerAnimation( + @BackNavigationInfo.BackTargetType int type, @NonNull BackAnimationRunner runner) { + mAnimationDefinition.set(type, runner); + } + + void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) { + mAnimationDefinition.remove(type); + } + + /** + * Start the {@link BackAnimationRunner} associated with a back target type. + * + * @param type back target type + * @return true if the animation is started, false if animation is not found for that type. + */ + boolean startGesture(@BackNavigationInfo.BackTargetType int type) { + BackAnimationRunner runner = mAnimationDefinition.get(type); + if (runner == null) { + return false; + } + runner.startGesture(); + return true; + } + + /** + * Cancel the {@link BackAnimationRunner} associated with a back target type. + * + * @param type back target type + * @return true if the animation is started, false if animation is not found for that type. + */ + boolean cancel(@BackNavigationInfo.BackTargetType int type) { + BackAnimationRunner runner = mAnimationDefinition.get(type); + if (runner == null) { + return false; + } + runner.cancelAnimation(); + return true; + } + + boolean isAnimationCancelledOrNull(@BackNavigationInfo.BackTargetType int type) { + BackAnimationRunner runner = mAnimationDefinition.get(type); + if (runner == null) { + return true; + } + return runner.isAnimationCancelled(); + } + + boolean isWaitingAnimation(@BackNavigationInfo.BackTargetType int type) { + BackAnimationRunner runner = mAnimationDefinition.get(type); + if (runner == null) { + return false; + } + return runner.isWaitingAnimation(); + } + + void resetDefaultCrossActivity() { + if (mDefaultCrossActivityAnimation == null + || !mAnimationDefinition.contains(BackNavigationInfo.TYPE_CROSS_ACTIVITY)) { + return; + } + mAnimationDefinition.set( + BackNavigationInfo.TYPE_CROSS_ACTIVITY, mDefaultCrossActivityAnimation.getRunner()); + } + + BackAnimationRunner getAnimationRunnerAndInit(BackNavigationInfo backNavigationInfo) { + int type = backNavigationInfo.getType(); + // Initiate customized cross-activity animation, or fall back to cross activity animation + if (type == BackNavigationInfo.TYPE_CROSS_ACTIVITY && mAnimationDefinition.contains(type)) { + if (mCustomizeActivityAnimation != null + && mCustomizeActivityAnimation.prepareNextAnimation( + backNavigationInfo.getCustomAnimationInfo())) { + mAnimationDefinition.get(type).resetWaitingAnimation(); + mAnimationDefinition.set( + BackNavigationInfo.TYPE_CROSS_ACTIVITY, + mCustomizeActivityAnimation.getRunner()); + } + } + BackAnimationRunner runner = mAnimationDefinition.get(type); + if (runner == null) { + Log.e( + TAG, + "Animation didn't be defined for type " + + BackNavigationInfo.typeToString(type)); + } + return runner; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TEST_MAPPING b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TEST_MAPPING index 837d5ff3b073..f02559f36169 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TEST_MAPPING +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TEST_MAPPING @@ -12,19 +12,19 @@ ] }, { - "name": "CtsWindowManagerDeviceTestCases", + "name": "CtsWindowManagerDeviceBackNavigation", "options": [ { "exclude-annotation": "androidx.test.filters.FlakyTest" }, { - "include-filter": "android.server.wm.BackGestureInvokedTest" + "include-filter": "android.server.wm.backnavigation.BackGestureInvokedTest" }, { - "include-filter": "android.server.wm.BackNavigationTests" + "include-filter": "android.server.wm.backnavigation.BackNavigationTests" }, { - "include-filter": "android.server.wm.OnBackInvokedCallbackGestureTest" + "include-filter": "android.server.wm.backnavigation.OnBackInvokedCallbackGestureTest" } ] } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java index f1ee8fa38485..a67821b7e819 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java @@ -318,7 +318,7 @@ public class BadgedImageView extends ConstraintLayout { /** * Animates the dot to the given scale, running the optional callback when the animation ends. */ - private void animateDotScale(float toScale, @Nullable Runnable after) { + public void animateDotScale(float toScale, @Nullable Runnable after) { mDotIsAnimating = true; // Don't restart the animation if we're already animating to the given value. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 9a2b81243861..85ea8097a2c1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -973,9 +973,9 @@ public class Bubble implements BubbleViewProvider { pw.print(" suppressNotif: "); pw.println(shouldSuppressNotification()); pw.print(" autoExpand: "); pw.println(shouldAutoExpand()); pw.print(" isDismissable: "); pw.println(mIsDismissable); - pw.println(" bubbleMetadataFlagListener null: " + (mBubbleMetadataFlagListener == null)); + pw.println(" bubbleMetadataFlagListener null?: " + (mBubbleMetadataFlagListener == null)); if (mExpandedView != null) { - mExpandedView.dump(pw); + mExpandedView.dump(pw, " "); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 8400ddec0af5..dddcbd4c96c0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -16,7 +16,6 @@ package com.android.wm.shell.bubbles; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; import static android.service.notification.NotificationListenerService.REASON_CANCEL; @@ -57,6 +56,7 @@ import android.content.pm.ShortcutInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Icon; import android.os.Binder; @@ -115,6 +115,7 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.taskview.TaskView; import com.android.wm.shell.taskview.TaskViewTransitions; +import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.util.ArrayList; @@ -143,6 +144,8 @@ public class BubbleController implements ConfigurationChangeListener, // Should match with PhoneWindowManager private static final String SYSTEM_DIALOG_REASON_KEY = "reason"; private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav"; + private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; + private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; /** * Common interface to send updates to bubble views. @@ -182,6 +185,7 @@ public class BubbleController implements ConfigurationChangeListener, private final ShellTaskOrganizer mTaskOrganizer; private final DisplayController mDisplayController; private final TaskViewTransitions mTaskViewTransitions; + private final Transitions mTransitions; private final SyncTransactionQueue mSyncQueue; private final ShellController mShellController; private final ShellCommandHandler mShellCommandHandler; @@ -282,6 +286,7 @@ public class BubbleController implements ConfigurationChangeListener, @ShellMainThread Handler mainHandler, @ShellBackgroundThread ShellExecutor bgExecutor, TaskViewTransitions taskViewTransitions, + Transitions transitions, SyncTransactionQueue syncQueue, IWindowManager wmService, BubbleProperties bubbleProperties) { @@ -317,6 +322,7 @@ public class BubbleController implements ConfigurationChangeListener, com.android.internal.R.dimen.importance_ring_stroke_width)); mDisplayController = displayController; mTaskViewTransitions = taskViewTransitions; + mTransitions = transitions; mOneHandedOptional = oneHandedOptional; mDragAndDropController = dragAndDropController; mSyncQueue = syncQueue; @@ -416,23 +422,9 @@ public class BubbleController implements ConfigurationChangeListener, } }, mMainHandler); - mTaskStackListener.addListener(new TaskStackListenerCallback() { - @Override - public void onTaskMovedToFront(int taskId) { - mMainExecutor.execute(() -> { - int expandedId = INVALID_TASK_ID; - if (mStackView != null && mStackView.getExpandedBubble() != null - && isStackExpanded() - && !mStackView.isExpansionAnimating() - && !mStackView.isSwitchAnimating()) { - expandedId = mStackView.getExpandedBubble().getTaskId(); - } - if (expandedId != INVALID_TASK_ID && expandedId != taskId) { - mBubbleData.setExpanded(false); - } - }); - } + mTransitions.registerObserver(new BubblesTransitionObserver(this, mBubbleData)); + mTaskStackListener.addListener(new TaskStackListenerCallback() { @Override public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { @@ -520,6 +512,7 @@ public class BubbleController implements ConfigurationChangeListener, * <p>If bubble bar is supported, bubble views will be updated to switch to bar mode. */ public void registerBubbleStateListener(Bubbles.BubbleStateListener listener) { + mBubbleProperties.refresh(); if (canShowAsBubbleBar() && listener != null) { // Only set the listener if we can show the bubble bar. mBubbleStateListener = listener; @@ -537,6 +530,7 @@ public class BubbleController implements ConfigurationChangeListener, * will be updated accordingly. */ public void unregisterBubbleStateListener() { + mBubbleProperties.refresh(); if (mBubbleStateListener != null) { mBubbleStateListener = null; setUpBubbleViewsForMode(); @@ -883,8 +877,10 @@ public class BubbleController implements ConfigurationChangeListener, String action = intent.getAction(); String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY); - if ((Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) - && SYSTEM_DIALOG_REASON_GESTURE_NAV.equals(reason)) + boolean validReasonToCollapse = SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason) + || SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason) + || SYSTEM_DIALOG_REASON_GESTURE_NAV.equals(reason); + if ((Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) && validReasonToCollapse) || Intent.ACTION_SCREEN_OFF.equals(action)) { mMainExecutor.execute(() -> collapseStack()); } @@ -1070,6 +1066,15 @@ public class BubbleController implements ConfigurationChangeListener, } } + /** + * Show bubble bar user education relative to the reference position. + * @param position the reference position in Screen coordinates. + */ + public void showUserEducation(Point position) { + if (mLayerView == null) return; + mLayerView.showUserEducation(position); + } + @VisibleForTesting public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) { boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key) @@ -1122,6 +1127,16 @@ public class BubbleController implements ConfigurationChangeListener, } /** + * Expands the stack if the selected bubble is present. This is currently used when user + * education view is clicked to expand the selected bubble. + */ + public void expandStackWithSelectedBubble() { + if (mBubbleData.getSelectedBubble() != null) { + mBubbleData.setExpanded(true); + } + } + + /** * Expands and selects the provided bubble as long as it already exists in the stack or the * overflow. This is currently used when opening a bubble via clicking on a conversation widget. */ @@ -1737,7 +1752,8 @@ public class BubbleController implements ConfigurationChangeListener, + " expandedChanged=" + update.expandedChanged + " selectionChanged=" + update.selectionChanged + " suppressed=" + (update.suppressedBubble != null) - + " unsuppressed=" + (update.unsuppressedBubble != null)); + + " unsuppressed=" + (update.unsuppressedBubble != null) + + " shouldShowEducation=" + update.shouldShowEducation); } ensureBubbleViewsAndWindowCreated(); @@ -1961,6 +1977,15 @@ public class BubbleController implements ConfigurationChangeListener, } } + /** + * Returns whether the stack is animating or not. + */ + public boolean isStackAnimating() { + return mStackView != null + && (mStackView.isExpansionAnimating() + || mStackView.isSwitchAnimating()); + } + @VisibleForTesting @Nullable public BubbleStackView getStackView() { @@ -1989,13 +2014,20 @@ public class BubbleController implements ConfigurationChangeListener, * Description of current bubble state. */ private void dump(PrintWriter pw, String prefix) { - pw.println("BubbleController state:"); + pw.print(prefix); pw.println("BubbleController state:"); + pw.print(prefix); pw.println(" currentUserId= " + mCurrentUserId); + pw.print(prefix); pw.println(" isStatusBarShade= " + mIsStatusBarShade); + pw.print(prefix); pw.println(" isShowingAsBubbleBar= " + isShowingAsBubbleBar()); + pw.println(); + mBubbleData.dump(pw); pw.println(); + if (mStackView != null) { mStackView.dump(pw); } pw.println(); + mImpl.mCachedState.dump(pw); } @@ -2146,6 +2178,12 @@ public class BubbleController implements ConfigurationChangeListener, public void onBubbleDrag(String bubbleKey, boolean isBeingDragged) { mMainExecutor.execute(() -> mController.onBubbleDrag(bubbleKey, isBeingDragged)); } + + @Override + public void showUserEducation(int positionX, int positionY) { + mMainExecutor.execute(() -> + mController.showUserEducation(new Point(positionX, positionY))); + } } private class BubblesImpl implements Bubbles { @@ -2244,8 +2282,7 @@ public class BubbleController implements ConfigurationChangeListener, pw.println("mIsStackExpanded: " + mIsStackExpanded); pw.println("mSelectedBubbleKey: " + mSelectedBubbleKey); - pw.print("mSuppressedBubbleKeys: "); - pw.println(mSuppressedBubbleKeys.size()); + pw.println("mSuppressedBubbleKeys: " + mSuppressedBubbleKeys.size()); for (String key : mSuppressedBubbleKeys) { pw.println(" suppressing: " + key); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index cc8f50e09fcb..595a4afbfc86 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -77,6 +77,7 @@ public class BubbleData { boolean orderChanged; boolean suppressedSummaryChanged; boolean expanded; + boolean shouldShowEducation; @Nullable BubbleViewProvider selectedBubble; @Nullable Bubble addedBubble; @Nullable Bubble updatedBubble; @@ -126,6 +127,7 @@ public class BubbleData { bubbleBarUpdate.expandedChanged = expandedChanged; bubbleBarUpdate.expanded = expanded; + bubbleBarUpdate.shouldShowEducation = shouldShowEducation; if (selectionChanged) { bubbleBarUpdate.selectedBubbleKey = selectedBubble != null ? selectedBubble.getKey() @@ -165,6 +167,7 @@ public class BubbleData { */ BubbleBarUpdate getInitialState() { BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate(); + bubbleBarUpdate.shouldShowEducation = shouldShowEducation; for (int i = 0; i < bubbles.size(); i++) { bubbleBarUpdate.currentBubbleList.add(bubbles.get(i).asBubbleBarBubble()); } @@ -187,6 +190,7 @@ public class BubbleData { private final Context mContext; private final BubblePositioner mPositioner; + private final BubbleEducationController mEducationController; private final Executor mMainExecutor; /** Bubbles that are actively in the stack. */ private final List<Bubble> mBubbles; @@ -233,10 +237,11 @@ public class BubbleData { private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>(); public BubbleData(Context context, BubbleLogger bubbleLogger, BubblePositioner positioner, - Executor mainExecutor) { + BubbleEducationController educationController, Executor mainExecutor) { mContext = context; mLogger = bubbleLogger; mPositioner = positioner; + mEducationController = educationController; mMainExecutor = mainExecutor; mOverflow = new BubbleOverflow(context, positioner); mBubbles = new ArrayList<>(); @@ -447,6 +452,7 @@ public class BubbleData { if (bubble.shouldAutoExpand()) { bubble.setShouldAutoExpand(false); setSelectedBubbleInternal(bubble); + if (!mExpanded) { setExpandedInternal(true); } @@ -877,6 +883,9 @@ public class BubbleData { private void dispatchPendingChanges() { if (mListener != null && mStateChange.anythingChanged()) { + mStateChange.shouldShowEducation = mSelectedBubble != null + && mEducationController.shouldShowStackEducation(mSelectedBubble) + && !mExpanded; mListener.applyUpdate(mStateChange); } mStateChange = new Update(mBubbles, mOverflowBubbles); @@ -1231,29 +1240,30 @@ public class BubbleData { * Description of current bubble data state. */ public void dump(PrintWriter pw) { - pw.print("selected: "); + pw.println("BubbleData state:"); + pw.print(" selected: "); pw.println(mSelectedBubble != null ? mSelectedBubble.getKey() : "null"); - pw.print("expanded: "); + pw.print(" expanded: "); pw.println(mExpanded); - pw.print("stack bubble count: "); + pw.print("Stack bubble count: "); pw.println(mBubbles.size()); for (Bubble bubble : mBubbles) { bubble.dump(pw); } - pw.print("overflow bubble count: "); + pw.print("Overflow bubble count: "); pw.println(mOverflowBubbles.size()); for (Bubble bubble : mOverflowBubbles) { bubble.dump(pw); } - pw.print("summaryKeys: "); + pw.print("SummaryKeys: "); pw.println(mSuppressedGroupKeys.size()); for (String key : mSuppressedGroupKeys.keySet()) { - pw.println(" suppressing: " + key); + pw.println(" suppressing: " + key); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java index 76662c47238f..f56b1712c5c1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java @@ -44,7 +44,7 @@ public class BubbleDebugConfig { static final boolean DEBUG_BUBBLE_EXPANDED_VIEW = false; static final boolean DEBUG_EXPERIMENTS = true; static final boolean DEBUG_OVERFLOW = false; - static final boolean DEBUG_USER_EDUCATION = false; + public static final boolean DEBUG_USER_EDUCATION = false; static final boolean DEBUG_POSITIONER = false; public static final boolean DEBUG_COLLAPSE_ANIMATOR = false; public static boolean DEBUG_EXPANDED_VIEW_DRAGGING = false; @@ -77,20 +77,25 @@ public class BubbleDebugConfig { static String formatBubblesString(List<Bubble> bubbles, BubbleViewProvider selected) { StringBuilder sb = new StringBuilder(); - for (Bubble bubble : bubbles) { + for (int i = 0; i < bubbles.size(); i++) { + Bubble bubble = bubbles.get(i); if (bubble == null) { - sb.append(" <null> !!!!!\n"); + sb.append(" <null> !!!!!"); } else { boolean isSelected = (selected != null - && selected.getKey() != BubbleOverflow.KEY + && !BubbleOverflow.KEY.equals(selected.getKey()) && bubble == selected); String arrow = isSelected ? "=>" : " "; - sb.append(String.format("%s Bubble{act=%12d, showInShade=%d, key=%s}\n", + + sb.append(String.format("%s Bubble{act=%12d, showInShade=%d, key=%s}", arrow, bubble.getLastActivity(), (bubble.showInShade() ? 1 : 0), bubble.getKey())); } + if (i != bubbles.size() - 1) { + sb.append("\n"); + } } return sb.toString(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index e6986012dd88..37bcf1ddeac5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -1094,9 +1094,9 @@ public class BubbleExpandedView extends LinearLayout { /** * Description of current expanded view state. */ - public void dump(@NonNull PrintWriter pw) { - pw.print("BubbleExpandedView"); - pw.print(" taskId: "); pw.println(mTaskId); - pw.print(" stackView: "); pw.println(mStackView); + public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { + pw.print(prefix); pw.println("BubbleExpandedView:"); + pw.print(prefix); pw.print(" taskId: "); pw.println(mTaskId); + pw.print(prefix); pw.print(" stackView: "); pw.println(mStackView); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt index df19757203eb..dc099d9abda4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt @@ -27,6 +27,7 @@ import android.graphics.drawable.ColorDrawable import android.graphics.drawable.InsetDrawable import android.util.PathParser import android.view.LayoutInflater +import android.view.View.VISIBLE import android.widget.FrameLayout import com.android.launcher3.icons.BubbleIconFactory import com.android.wm.shell.R @@ -156,7 +157,9 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl fun setShowDot(show: Boolean) { showDot = show - overflowBtn?.updateDotVisibility(true /* animate */) + if (overflowBtn?.visibility == VISIBLE) { + overflowBtn?.updateDotVisibility(true /* animate */) + } } /** Creates the expanded view for bubbles showing in the stack view. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index ea7053d8ee49..17e06e93b3a8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -54,10 +54,6 @@ public class BubblePositioner { public static final float FLYOUT_MAX_WIDTH_PERCENT_LARGE_SCREEN = 0.3f; /** The max percent of screen width to use for the flyout on phone. */ public static final float FLYOUT_MAX_WIDTH_PERCENT = 0.6f; - /** The percent of screen width for the expanded view on a large screen. **/ - private static final float EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT = 0.48f; - /** The percent of screen width for the expanded view on a large screen. **/ - private static final float EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT = 0.70f; /** The percent of screen width for the expanded view on a small tablet. **/ private static final float EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT = 0.72f; /** The percent of screen width for the expanded view when shown in the bubble bar. **/ @@ -95,6 +91,7 @@ public class BubblePositioner { private int mPointerWidth; private int mPointerHeight; private int mPointerOverlap; + private int mManageButtonHeightIncludingMargins; private int mManageButtonHeight; private int mOverflowHeight; private int mMinimumFlyoutWidthLargeScreen; @@ -176,21 +173,20 @@ public class BubblePositioner { mExpandedViewLargeScreenWidth = (int) (bounds.width() * EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT); } else { - mExpandedViewLargeScreenWidth = isLandscape() - ? (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT) - : (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT); + mExpandedViewLargeScreenWidth = + res.getDimensionPixelSize(R.dimen.bubble_expanded_view_largescreen_width); } if (mIsLargeScreen) { - if (isLandscape() && !mIsSmallTablet) { + if (mIsSmallTablet) { + final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2; + mExpandedViewLargeScreenInsetClosestEdge = centeredInset; + mExpandedViewLargeScreenInsetFurthestEdge = centeredInset; + } else { mExpandedViewLargeScreenInsetClosestEdge = res.getDimensionPixelSize( R.dimen.bubble_expanded_view_largescreen_landscape_padding); mExpandedViewLargeScreenInsetFurthestEdge = bounds.width() - mExpandedViewLargeScreenInsetClosestEdge - mExpandedViewLargeScreenWidth; - } else { - final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2; - mExpandedViewLargeScreenInsetClosestEdge = centeredInset; - mExpandedViewLargeScreenInsetFurthestEdge = centeredInset; } } else { mExpandedViewLargeScreenInsetClosestEdge = mExpandedViewPadding; @@ -202,7 +198,9 @@ public class BubblePositioner { mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); mPointerOverlap = res.getDimensionPixelSize(R.dimen.bubble_pointer_overlap); - mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height); + mManageButtonHeightIncludingMargins = + res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height); + mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height); mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); mMinimumFlyoutWidthLargeScreen = res.getDimensionPixelSize( @@ -420,7 +418,7 @@ public class BubblePositioner { int pointerSize = showBubblesVertically() ? mPointerWidth : (mPointerHeight + mPointerMargin); - int bottomPadding = isOverflow ? mExpandedViewPadding : mManageButtonHeight; + int bottomPadding = isOverflow ? mExpandedViewPadding : mManageButtonHeightIncludingMargins; return getAvailableRect().height() - expandedContainerY - paddingTop @@ -438,6 +436,15 @@ public class BubblePositioner { // overflow in landscape on phone is max return MAX_HEIGHT; } + + if (mIsLargeScreen && !mIsSmallTablet && !isOverflow) { + // the expanded view height on large tablets is calculated based on the shortest screen + // size and is the same in both portrait and landscape + int maxVerticalInset = Math.max(mInsets.top, mInsets.bottom); + int shortestScreenSide = Math.min(mScreenRect.height(), mScreenRect.width()); + return shortestScreenSide - 2 * maxVerticalInset - mManageButtonHeight; + } + float desiredHeight = isOverflow ? mOverflowHeight : ((Bubble) bubble).getDesiredHeight(mContext); @@ -466,7 +473,8 @@ public class BubblePositioner { return topAlignment; } // If we're here, we're showing vertically & developer has made height less than maximum. - int manageButtonHeight = isOverflow ? mExpandedViewPadding : mManageButtonHeight; + int manageButtonHeight = + isOverflow ? mExpandedViewPadding : mManageButtonHeightIncludingMargins; float pointerPosition = getPointerPosition(bubblePosition); float bottomIfCentered = pointerPosition + (expandedViewHeight / 2) + manageButtonHeight; float topIfCentered = pointerPosition - (expandedViewHeight / 2); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 52c9bf8462ec..ac5ba51ec139 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -25,6 +25,7 @@ import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_ import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING; +import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES; import android.animation.Animator; @@ -131,8 +132,6 @@ public class BubbleStackView extends FrameLayout private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150; - private static final float SCRIM_ALPHA = 0.32f; - /** Minimum alpha value for scrim when alpha is being changed via drag */ private static final float MIN_SCRIM_ALPHA_FOR_DRAG = 0.2f; @@ -305,8 +304,7 @@ public class BubbleStackView extends FrameLayout String bubblesOnScreen = BubbleDebugConfig.formatBubblesString( getBubblesOnScreen(), getExpandedBubble()); - pw.print(" stack visibility : "); pw.println(getVisibility()); - pw.print(" bubbles on screen: "); pw.println(bubblesOnScreen); + pw.println(" bubbles on screen: "); pw.println(bubblesOnScreen); pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress); pw.print(" showingDismiss: "); pw.println(mDismissView.isShowing()); pw.print(" isExpansionAnimating: "); pw.println(mIsExpansionAnimating); @@ -314,7 +312,8 @@ public class BubbleStackView extends FrameLayout pw.print(" expandedContainerAlpha: "); pw.println(mExpandedViewContainer.getAlpha()); pw.print(" expandedContainerMatrix: "); pw.println(mExpandedViewContainer.getAnimationMatrix()); - + pw.print(" stack visibility : "); pw.println(getVisibility()); + pw.print(" temporarilyInvisible: "); pw.println(mTemporarilyInvisible); mStackAnimationController.dump(pw); mExpandedAnimationController.dump(pw); @@ -779,14 +778,15 @@ public class BubbleStackView extends FrameLayout private float getScrimAlphaForDrag(float dragAmount) { // dragAmount should be negative as we allow scroll up only if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { - float alphaRange = SCRIM_ALPHA - MIN_SCRIM_ALPHA_FOR_DRAG; + float alphaRange = BUBBLE_EXPANDED_SCRIM_ALPHA - MIN_SCRIM_ALPHA_FOR_DRAG; int dragMax = mExpandedBubble.getExpandedView().getContentHeight(); float dragFraction = dragAmount / dragMax; - return Math.max(SCRIM_ALPHA - alphaRange * dragFraction, MIN_SCRIM_ALPHA_FOR_DRAG); + return Math.max(BUBBLE_EXPANDED_SCRIM_ALPHA - alphaRange * dragFraction, + MIN_SCRIM_ALPHA_FOR_DRAG); } - return SCRIM_ALPHA; + return BUBBLE_EXPANDED_SCRIM_ALPHA; } }; @@ -1784,13 +1784,14 @@ public class BubbleStackView extends FrameLayout mStackOnLeftOrWillBe = mPositioner.isStackOnLeft(startPosition); mStackAnimationController.setStackPosition(startPosition); mExpandedAnimationController.setCollapsePoint(startPosition); - // Set the translation x so that this bubble will animate in from the same side they - // expand / collapse on. - bubble.getIconView().setTranslationX(startPosition.x); } else if (firstBubble) { mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide(); } + // Set the view translation x so that this bubble will animate in from the same side they + // expand / collapse on. + bubble.getIconView().setTranslationX(mStackAnimationController.getStackPosition().x); + mBubbleContainer.addView(bubble.getIconView(), 0, new FrameLayout.LayoutParams(mPositioner.getBubbleSize(), mPositioner.getBubbleSize())); @@ -1864,6 +1865,14 @@ public class BubbleStackView extends FrameLayout : GONE); } + private void updateOverflowDotVisibility(boolean expanding) { + if (mBubbleOverflow.showDot()) { + mBubbleOverflow.getIconView().animateDotScale(expanding ? 1 : 0f, () -> { + mBubbleOverflow.setVisible(expanding ? VISIBLE : GONE); + }); + } + } + // via BubbleData.Listener void updateBubble(Bubble bubble) { animateInFlyoutForBubble(bubble); @@ -2037,6 +2046,7 @@ public class BubbleStackView extends FrameLayout }); } notifyExpansionChanged(mExpandedBubble, mIsExpanded); + announceExpandForAccessibility(mExpandedBubble, mIsExpanded); } /** @@ -2053,6 +2063,34 @@ public class BubbleStackView extends FrameLayout } } + private void announceExpandForAccessibility(BubbleViewProvider bubble, boolean expanded) { + if (bubble instanceof Bubble) { + String contentDescription = getBubbleContentDescription((Bubble) bubble); + String message = getResources().getString( + expanded + ? R.string.bubble_accessibility_announce_expand + : R.string.bubble_accessibility_announce_collapse, contentDescription); + announceForAccessibility(message); + } + } + + @NonNull + private String getBubbleContentDescription(Bubble bubble) { + final String appName = bubble.getAppName(); + final String title = bubble.getTitle() != null + ? bubble.getTitle() + : getResources().getString(R.string.notification_bubble_title); + + if (appName == null || title.equals(appName)) { + // App bubble title equals the app name, so return only the title to avoid having + // content description like: `<app> from <app>`. + return title; + } else { + return getResources().getString( + R.string.bubble_content_description_single, title, appName); + } + } + private boolean isGestureNavEnabled() { return mContext.getResources().getInteger( com.android.internal.R.integer.config_navBarInteractionMode) @@ -2214,7 +2252,7 @@ public class BubbleStackView extends FrameLayout if (show) { mScrim.animate() .setInterpolator(ALPHA_IN) - .alpha(SCRIM_ALPHA) + .alpha(BUBBLE_EXPANDED_SCRIM_ALPHA) .setListener(listener) .start(); } else { @@ -2245,6 +2283,7 @@ public class BubbleStackView extends FrameLayout if (mIsExpanded && mExpandedBubble.getExpandedView() != null) { maybeShowManageEdu(); } + updateOverflowDotVisibility(true /* expanding */); } /* after */); int index; if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) { @@ -2376,11 +2415,15 @@ public class BubbleStackView extends FrameLayout // since we're about to animate collapsed. mExpandedAnimationController.notifyPreparingToCollapse(); + updateOverflowDotVisibility(false /* expanding */); final Runnable collapseBackToStack = () -> mExpandedAnimationController.collapseBackToStack( mStackAnimationController .getStackPositionAlongNearestHorizontalEdge() /* collapseTo */, - () -> mBubbleContainer.setActiveController(mStackAnimationController)); + () -> { + mBubbleContainer.setActiveController(mStackAnimationController); + updateOverflowVisibility(); + }); final Runnable after = () -> { final BubbleViewProvider previouslySelected = mExpandedBubble; @@ -2395,7 +2438,6 @@ public class BubbleStackView extends FrameLayout Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(), mExpandedBubble)); } - updateOverflowVisibility(); updateZOrder(); updateBadges(true /* setBadgeForCollapsedStack */); afterExpandedViewAnimation(); @@ -2943,7 +2985,7 @@ public class BubbleStackView extends FrameLayout mBubbleController.getSysuiProxy().onManageMenuExpandChanged(show); mManageMenuScrim.animate() .setInterpolator(show ? ALPHA_IN : ALPHA_OUT) - .alpha(show ? SCRIM_ALPHA : 0f) + .alpha(show ? BUBBLE_EXPANDED_SCRIM_ALPHA : 0f) .withEndAction(endAction) .start(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index 39e3180ffe2a..bb30c5eeebcf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -155,14 +155,14 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask Bitmap rawBadgeBitmap; // Only populated when showing in taskbar - BubbleBarExpandedView bubbleBarExpandedView; + @Nullable BubbleBarExpandedView bubbleBarExpandedView; // These are only populated when not showing in taskbar - BadgedImageView imageView; - BubbleExpandedView expandedView; + @Nullable BadgedImageView imageView; + @Nullable BubbleExpandedView expandedView; int dotColor; Path dotPath; - Bubble.FlyoutMessage flyoutMessage; + @Nullable Bubble.FlyoutMessage flyoutMessage; Bitmap bubbleBitmap; Bitmap badgeBitmap; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java new file mode 100644 index 000000000000..9e8a385262e4 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.bubbles; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; + +import android.app.ActivityManager; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.window.TransitionInfo; + +import androidx.annotation.NonNull; + +import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.util.TransitionUtil; + +/** + * Observer used to identify tasks that are opening or moving to front. If a bubble activity is + * currently opened when this happens, we'll collapse the bubbles. + */ +public class BubblesTransitionObserver implements Transitions.TransitionObserver { + + private BubbleController mBubbleController; + private BubbleData mBubbleData; + + public BubblesTransitionObserver(BubbleController controller, + BubbleData bubbleData) { + mBubbleController = controller; + mBubbleData = bubbleData; + } + + @Override + public void onTransitionReady(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction) { + for (TransitionInfo.Change change : info.getChanges()) { + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); + // We only care about opens / move to fronts when bubbles are expanded & not animating. + if (taskInfo == null + || taskInfo.taskId == INVALID_TASK_ID + || !TransitionUtil.isOpeningType(change.getMode()) + || mBubbleController.isStackAnimating() + || !mBubbleData.isExpanded() + || mBubbleData.getSelectedBubble() == null) { + continue; + } + int expandedId = mBubbleData.getSelectedBubble().getTaskId(); + // If the task id that's opening is the same as the expanded bubble, skip collapsing + // because it is our bubble that is opening. + if (expandedId != INVALID_TASK_ID && expandedId != taskInfo.taskId) { + mBubbleData.setExpanded(false); + } + } + } + + @Override + public void onTransitionStarting(@NonNull IBinder transition) { + + } + + @Override + public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) { + + } + + @Override + public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) { + + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt index ed3624035757..48692d41016e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt @@ -22,6 +22,7 @@ import com.android.wm.shell.common.bubbles.DismissView fun DismissView.setup() { setup(DismissView.Config( + dismissViewResId = R.id.dismiss_view, targetSizeResId = R.dimen.dismiss_circle_size, iconSizeResId = R.dimen.dismiss_target_x_size, bottomMarginResId = R.dimen.floating_dismiss_bottom_margin, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl index 4dda0688b790..5776ad109d19 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl @@ -39,4 +39,6 @@ interface IBubbles { oneway void onBubbleDrag(in String key, in boolean isBeingDragged) = 7; + oneway void showUserEducation(in int positionX, in int positionY) = 8; + }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java index 4d7042bbb3d2..738c94e82a95 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java @@ -34,6 +34,8 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.animation.PhysicsAnimator; +import com.android.wm.shell.bubbles.BadgedImageView; +import com.android.wm.shell.bubbles.BubbleOverflow; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleStackView; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; @@ -63,6 +65,12 @@ public class ExpandedAnimationController /** Damping ratio for expand/collapse spring. */ private static final float DAMPING_RATIO_MEDIUM_LOW_BOUNCY = 0.65f; + /** + * Damping ratio for the overflow bubble spring; this is less bouncy so it doesn't bounce behind + * the top bubble when it goes to disappear. + */ + private static final float DAMPING_RATIO_OVERFLOW_BOUNCY = 0.90f; + /** Stiffness for the expand/collapse path-following animation. */ private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 400; @@ -274,9 +282,14 @@ public class ExpandedAnimationController // of the screen where the bubble will be stacked. path.lineTo(stackedX, p.y); + // The overflow should animate to the collapse point, so 0 offset. + final boolean isOverflow = bubble instanceof BadgedImageView + && BubbleOverflow.KEY.equals(((BadgedImageView) bubble).getKey()); + final float offsetY = isOverflow + ? 0 + : Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx; // Then, draw a line down to the stack position. - path.lineTo(stackedX, mCollapsePoint.y - + Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx); + path.lineTo(stackedX, mCollapsePoint.y + offsetY); } // The lead bubble should be the bubble with the longest distance to travel when we're @@ -505,8 +518,12 @@ public class ExpandedAnimationController @Override SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) { + boolean isOverflow = (view instanceof BadgedImageView) + && BubbleOverflow.KEY.equals(((BadgedImageView) view).getKey()); return new SpringForce() - .setDampingRatio(DAMPING_RATIO_MEDIUM_LOW_BOUNCY) + .setDampingRatio(isOverflow + ? DAMPING_RATIO_OVERFLOW_BOUNCY + : DAMPING_RATIO_MEDIUM_LOW_BOUNCY) .setStiffness(SpringForce.STIFFNESS_LOW); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index 8f11253290ea..e788341df5f8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -21,6 +21,7 @@ import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT; import android.annotation.Nullable; import android.content.Context; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.graphics.drawable.ColorDrawable; @@ -36,6 +37,8 @@ import com.android.wm.shell.bubbles.BubbleViewProvider; import java.util.function.Consumer; +import kotlin.Unit; + /** * Similar to {@link com.android.wm.shell.bubbles.BubbleStackView}, this view is added to window * manager to display bubbles. However, it is only used when bubbles are being displayed in @@ -111,7 +114,7 @@ public class BubbleBarLayerView extends FrameLayout getViewTreeObserver().removeOnComputeInternalInsetsListener(this); if (mExpandedView != null) { - mEducationViewController.hideManageEducation(/* animated = */ false); + mEducationViewController.hideEducation(/* animated = */ false); removeView(mExpandedView); mExpandedView = null; } @@ -171,7 +174,9 @@ public class BubbleBarLayerView extends FrameLayout mExpandedView.setListener(new BubbleBarExpandedView.Listener() { @Override public void onTaskCreated() { - mEducationViewController.maybeShowManageEducation(b, mExpandedView); + if (mEducationViewController != null && mExpandedView != null) { + mEducationViewController.maybeShowManageEducation(b, mExpandedView); + } } @Override @@ -190,6 +195,10 @@ public class BubbleBarLayerView extends FrameLayout addView(mExpandedView, new FrameLayout.LayoutParams(width, height)); } + if (mEducationViewController.isEducationVisible()) { + mEducationViewController.hideEducation(/* animated = */ true); + } + mIsExpanded = true; mBubbleController.getSysuiProxy().onStackExpandChanged(true); mAnimationHelper.animateExpansion(mExpandedBubble, () -> { @@ -210,7 +219,7 @@ public class BubbleBarLayerView extends FrameLayout public void collapse() { mIsExpanded = false; final BubbleBarExpandedView viewToRemove = mExpandedView; - mEducationViewController.hideManageEducation(/* animated = */ true); + mEducationViewController.hideEducation(/* animated = */ true); mAnimationHelper.animateCollapse(() -> removeView(viewToRemove)); mBubbleController.getSysuiProxy().onStackExpandChanged(false); mExpandedView = null; @@ -218,6 +227,21 @@ public class BubbleBarLayerView extends FrameLayout showScrim(false); } + /** + * Show bubble bar user education relative to the reference position. + * @param position the reference position in Screen coordinates. + */ + public void showUserEducation(Point position) { + mEducationViewController.showStackEducation(position, /* root = */ this, () -> { + // When the user education is clicked hide it and expand the selected bubble + mEducationViewController.hideEducation(/* animated = */ true, () -> { + mBubbleController.expandStackWithSelectedBubble(); + return Unit.INSTANCE; + }); + return Unit.INSTANCE; + }); + } + /** Sets the function to call to un-bubble the given conversation. */ public void setUnBubbleConversationCallback( @Nullable Consumer<String> unBubbleConversationCallback) { @@ -226,8 +250,8 @@ public class BubbleBarLayerView extends FrameLayout /** Hides the current modal education/menu view, expanded view or collapses the bubble stack */ private void hideMenuOrCollapse() { - if (mEducationViewController.isManageEducationVisible()) { - mEducationViewController.hideManageEducation(/* animated = */ true); + if (mEducationViewController.isEducationVisible()) { + mEducationViewController.hideEducation(/* animated = */ true); } else if (isExpanded() && mExpandedView != null) { mExpandedView.hideMenuOrCollapse(); } else { @@ -275,7 +299,7 @@ public class BubbleBarLayerView extends FrameLayout */ private void getTouchableRegion(Region outRegion) { mTempRect.setEmpty(); - if (mIsExpanded) { + if (mIsExpanded || mEducationViewController.isEducationVisible()) { getBoundsOnScreen(mTempRect); outRegion.op(mTempRect, Region.Op.UNION); } 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 7b39c6fd4059..ee552ae204b8 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 @@ -15,23 +15,34 @@ */ package com.android.wm.shell.bubbles.bar +import android.annotation.LayoutRes import android.content.Context +import android.graphics.Point +import android.graphics.Rect +import android.util.Log import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout import androidx.core.view.doOnLayout import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.SpringForce import com.android.wm.shell.R import com.android.wm.shell.animation.PhysicsAnimator +import com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION +import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES +import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME import com.android.wm.shell.bubbles.BubbleEducationController import com.android.wm.shell.bubbles.BubbleViewProvider import com.android.wm.shell.bubbles.setup +import com.android.wm.shell.common.bubbles.BubblePopupDrawable import com.android.wm.shell.common.bubbles.BubblePopupView +import kotlin.math.roundToInt /** Manages bubble education presentation and animation */ class BubbleEducationViewController(private val context: Context, private val listener: Listener) { interface Listener { - fun onManageEducationVisibilityChanged(isVisible: Boolean) + fun onEducationVisibilityChanged(isVisible: Boolean) } private var rootView: ViewGroup? = null @@ -45,61 +56,112 @@ class BubbleEducationViewController(private val context: Context, private val li ) } + private val scrimView by lazy { + View(context).apply { + importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO + setOnClickListener { hideEducation(animated = true) } + } + } + private val controller by lazy { BubbleEducationController(context) } /** Whether the education view is visible or being animated */ - val isManageEducationVisible: Boolean + val isEducationVisible: Boolean get() = educationView != null && rootView != null /** - * Show manage bubble education if hasn't been shown before + * Hide the current education view if visible * - * @param bubble the bubble used for the manage education check - * @param root the view to show manage education in + * @param animated whether should hide with animation */ - fun maybeShowManageEducation(bubble: BubbleViewProvider, root: ViewGroup) { - if (!controller.shouldShowManageEducation(bubble)) return - showManageEducation(root) + @JvmOverloads + fun hideEducation(animated: Boolean, endActions: () -> Unit = {}) { + log { "hideEducation animated: $animated" } + + if (animated) { + animateTransition(show = false) { + cleanUp() + endActions() + listener.onEducationVisibilityChanged(isVisible = false) + } + } else { + cleanUp() + endActions() + listener.onEducationVisibilityChanged(isVisible = false) + } } /** - * Hide the manage education view if visible + * Show bubble bar stack user education. * - * @param animated whether should hide with animation + * @param position the reference position for the user education in Screen coordinates. + * @param root the view to show user education in. + * @param educationClickHandler the on click handler for the user education view */ - fun hideManageEducation(animated: Boolean) { - rootView?.let { - fun cleanUp() { - it.removeView(educationView) - rootView = null - listener.onManageEducationVisibilityChanged(isVisible = false) - } + fun showStackEducation(position: Point, root: ViewGroup, educationClickHandler: () -> Unit) { + hideEducation(animated = false) + log { "showStackEducation at: $position" } - if (animated) { - animateTransition(show = false, ::cleanUp) - } else { - cleanUp() + educationView = + createEducationView(R.layout.bubble_bar_stack_education, root).apply { + setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN) + setArrowPosition(BubblePopupDrawable.ArrowPosition.End) + updateEducationPosition(view = this, position, root) + val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f + doOnLayout { + it.pivotX = it.width - arrowToEdgeOffset + it.pivotY = it.height.toFloat() + } + setOnClickListener { educationClickHandler() } } + + rootView = root + animator = createAnimator() + + root.addView(scrimView) + root.addView(educationView) + animateTransition(show = true) { + controller.hasSeenStackEducation = true + listener.onEducationVisibilityChanged(isVisible = true) } } /** + * Show manage bubble education if hasn't been shown before + * + * @param bubble the bubble used for the manage education check + * @param root the view to show manage education in + */ + fun maybeShowManageEducation(bubble: BubbleViewProvider, root: ViewGroup) { + log { "maybeShowManageEducation bubble: $bubble" } + if (!controller.shouldShowManageEducation(bubble)) return + showManageEducation(root) + } + + /** * Show manage education with animation * * @param root the view to show manage education in */ private fun showManageEducation(root: ViewGroup) { - hideManageEducation(animated = false) - if (educationView == null) { - val eduView = createEducationView(root) - educationView = eduView - animator = createAnimation(eduView) - } - root.addView(educationView) + hideEducation(animated = false) + log { "showManageEducation" } + + educationView = + createEducationView(R.layout.bubble_bar_manage_education, root).apply { + pivotY = 0f + doOnLayout { it.pivotX = it.width / 2f } + setOnClickListener { hideEducation(animated = true) } + } + rootView = root + animator = createAnimator() + + root.addView(scrimView) + root.addView(educationView) animateTransition(show = true) { controller.hasSeenManageEducation = true - listener.onManageEducationVisibilityChanged(isVisible = true) + listener.onEducationVisibilityChanged(isVisible = true) } } @@ -110,39 +172,75 @@ class BubbleEducationViewController(private val context: Context, private val li * @param endActions a closure to be called when the animation completes */ private fun animateTransition(show: Boolean, endActions: () -> Unit) { - animator?.let { animator -> - animator - .spring(DynamicAnimation.ALPHA, if (show) 1f else 0f) - .spring(DynamicAnimation.SCALE_X, if (show) 1f else EDU_SCALE_HIDDEN) - .spring(DynamicAnimation.SCALE_Y, if (show) 1f else EDU_SCALE_HIDDEN) - .withEndActions(endActions) - .start() - } ?: endActions() + animator + ?.spring(DynamicAnimation.ALPHA, if (show) 1f else 0f) + ?.spring(DynamicAnimation.SCALE_X, if (show) 1f else EDU_SCALE_HIDDEN) + ?.spring(DynamicAnimation.SCALE_Y, if (show) 1f else EDU_SCALE_HIDDEN) + ?.withEndActions(endActions) + ?.start() + ?: endActions() + } + + /** Remove education view from the root and clean up all relative properties */ + private fun cleanUp() { + log { "cleanUp" } + rootView?.removeView(educationView) + rootView?.removeView(scrimView) + educationView = null + rootView = null + animator = null } - private fun createEducationView(root: ViewGroup): BubblePopupView { - val view = - LayoutInflater.from(context).inflate(R.layout.bubble_bar_manage_education, root, false) - as BubblePopupView - - return view.apply { - setup() - alpha = 0f - pivotY = 0f - scaleX = EDU_SCALE_HIDDEN - scaleY = EDU_SCALE_HIDDEN - doOnLayout { it.pivotX = it.width / 2f } - setOnClickListener { hideManageEducation(animated = true) } + /** + * Create education view by inflating layout provided. + * + * @param layout layout resource id to inflate. The root view should be [BubblePopupView] + * @param root view group to use as root for inflation, is not attached to root + */ + private fun createEducationView(@LayoutRes layout: Int, root: ViewGroup): BubblePopupView { + val view = LayoutInflater.from(context).inflate(layout, root, false) as BubblePopupView + view.setup() + view.alpha = 0f + view.scaleX = EDU_SCALE_HIDDEN + view.scaleY = EDU_SCALE_HIDDEN + return view + } + + /** Create animator for the user education transitions */ + private fun createAnimator(): PhysicsAnimator<BubblePopupView>? { + return educationView?.let { + PhysicsAnimator.getInstance(it).apply { setDefaultSpringConfig(springConfig) } } } - private fun createAnimation(view: BubblePopupView): PhysicsAnimator<BubblePopupView> { - val animator = PhysicsAnimator.getInstance(view) - animator.setDefaultSpringConfig(springConfig) - return animator + /** + * Update user education view position relative to the reference position + * + * @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 + */ + 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) + // 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() + ?: 0 + // Calculate education view margins + val params = view.layoutParams as FrameLayout.LayoutParams + params.bottomMargin = rootBounds.bottom - position.y + params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset + view.layoutParams = params + } + + private fun log(msg: () -> String) { + if (DEBUG_USER_EDUCATION) Log.d(TAG, msg()) } companion object { + private val TAG = if (TAG_WITH_CLASS_NAME) "BubbleEducationViewController" else TAG_BUBBLES private const val EDU_SCALE_HIDDEN = 0.5f } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt index 85aaa8ef585c..4206d9320b7d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt @@ -29,4 +29,7 @@ interface BubbleProperties { * When this is `false`, bubbles will be floating. */ val isBubbleBarEnabled: Boolean + + /** Refreshes the current value of [isBubbleBarEnabled]. */ + fun refresh() } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt index 9d8b9a6f3260..e1dea3babbc2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt @@ -22,6 +22,13 @@ import android.os.SystemProperties object ProdBubbleProperties : BubbleProperties { // TODO(b/256873975) Should use proper flag when available to shell/launcher - override val isBubbleBarEnabled = - SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false) + private var _isBubbleBarEnabled = + SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false) + + override val isBubbleBarEnabled + get() = _isBubbleBarEnabled + + override fun refresh() { + _isBubbleBarEnabled = SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false) + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java index 72702e7c2b88..b828aac39040 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java @@ -18,6 +18,7 @@ package com.android.wm.shell.common; import android.annotation.Nullable; import android.os.RemoteException; +import android.os.Trace; import android.util.Slog; import android.view.IDisplayChangeWindowCallback; import android.view.IDisplayChangeWindowController; @@ -40,6 +41,7 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public class DisplayChangeController { private static final String TAG = DisplayChangeController.class.getSimpleName(); + private static final String HANDLE_DISPLAY_CHANGE_TRACE_TAG = "HandleRemoteDisplayChange"; private final ShellExecutor mMainExecutor; private final IWindowManager mWmService; @@ -81,9 +83,15 @@ public class DisplayChangeController { /** Query all listeners for changes that should happen on display change. */ void dispatchOnDisplayChange(WindowContainerTransaction outWct, int displayId, int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { + Trace.beginSection("dispatchOnDisplayChange"); + } for (OnDisplayChangingListener c : mDisplayChangeListener) { c.onDisplayChange(displayId, fromRotation, toRotation, newDisplayAreaInfo, outWct); } + if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { + Trace.endSection(); + } } private void onDisplayChange(int displayId, int fromRotation, int toRotation, @@ -94,6 +102,10 @@ public class DisplayChangeController { callback.continueDisplayChange(t); } catch (RemoteException e) { Slog.e(TAG, "Failed to continue handling display change", e); + } finally { + if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { + Trace.endAsyncSection(HANDLE_DISPLAY_CHANGE_TRACE_TAG, callback.hashCode()); + } } } @@ -103,6 +115,9 @@ public class DisplayChangeController { @Override public void onDisplayChange(int displayId, int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { + Trace.beginAsyncSection(HANDLE_DISPLAY_CHANGE_TRACE_TAG, callback.hashCode()); + } mMainExecutor.execute(() -> DisplayChangeController.this .onDisplayChange(displayId, fromRotation, toRotation, newDisplayAreaInfo, callback)); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS index 7af038999797..6519eee18e69 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS @@ -1 +1,2 @@ madym@google.com +hwwang@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index 5e42782431fd..e9344ffcce0c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -203,7 +203,7 @@ public class SystemWindows { + "SystemWindow:" + view); return null; } - return root.getFocusGrantToken(); + return root.getInputTransferToken(); } private class PerDisplay { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java index 931cf0cee28c..c6c9b3562308 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java @@ -94,6 +94,10 @@ public class TvWindowMenuActionButton extends RelativeLayout { mCurrentIcon = icon; // Remove old image while waiting for the new one to load. mIconImageView.setImageDrawable(null); + if (icon.getType() == Icon.TYPE_URI || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) { + // Disallow loading icon from content URI + return; + } icon.loadDrawableAsync(mContext, d -> { // The image hasn't been set any other way and the drawable belongs to the most // recently set Icon. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java index 81423473171d..fc627a8dcb36 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java @@ -35,6 +35,7 @@ public class BubbleBarUpdate implements Parcelable { public boolean expandedChanged; public boolean expanded; + public boolean shouldShowEducation; @Nullable public String selectedBubbleKey; @Nullable @@ -61,6 +62,7 @@ public class BubbleBarUpdate implements Parcelable { public BubbleBarUpdate(Parcel parcel) { expandedChanged = parcel.readBoolean(); expanded = parcel.readBoolean(); + shouldShowEducation = parcel.readBoolean(); selectedBubbleKey = parcel.readString(); addedBubble = parcel.readParcelable(BubbleInfo.class.getClassLoader(), BubbleInfo.class); @@ -95,6 +97,7 @@ public class BubbleBarUpdate implements Parcelable { return "BubbleBarUpdate{ expandedChanged=" + expandedChanged + " expanded=" + expanded + " selectedBubbleKey=" + selectedBubbleKey + + " shouldShowEducation=" + shouldShowEducation + " addedBubble=" + addedBubble + " updatedBubble=" + updatedBubble + " suppressedBubbleKey=" + suppressedBubbleKey @@ -114,6 +117,7 @@ public class BubbleBarUpdate implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeBoolean(expandedChanged); parcel.writeBoolean(expanded); + parcel.writeBoolean(shouldShowEducation); parcel.writeString(selectedBubbleKey); parcel.writeParcelable(addedBubble, flags); parcel.writeParcelable(updatedBubble, flags); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java index 6a46d23ad2a1..0329b8df7544 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java @@ -14,14 +14,13 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.bubble +package com.android.wm.shell.common.bubbles; -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +/** + * Constants shared between bubbles in shell & things we have to do for bubbles in launcher. + */ +public class BubbleConstants { -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -class OpenActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) : - OpenActivityFromBubbleTest(flicker) + /** The alpha for the scrim shown when bubbles are expanded. */ + public static float BUBBLE_EXPANDED_SCRIM_ALPHA = .32f; +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt index 1fd22d0a3505..887af17c9653 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt @@ -31,7 +31,7 @@ import kotlin.math.sin import kotlin.properties.Delegates /** A drawable for the [BubblePopupView] that draws a popup background with a directional arrow */ -class BubblePopupDrawable(private val config: Config) : Drawable() { +class BubblePopupDrawable(val config: Config) : Drawable() { /** The direction of the arrow in the popup drawable */ enum class ArrowDirection { UP, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt index f8a4946bb5c5..444fbf7884be 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt @@ -29,7 +29,8 @@ constructor( defStyleAttr: Int = 0, defStyleRes: Int = 0 ) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) { - private var popupDrawable: BubblePopupDrawable? = null + var popupDrawable: BubblePopupDrawable? = null + private set /** * Sets up the popup drawable with the config provided. Required to remove dependency on local diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt index d275a0be8e93..2eb55e19a960 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt @@ -49,6 +49,8 @@ class DismissView(context: Context) : FrameLayout(context) { * @see [setup] method */ data class Config( + /** The resource id to set on the dismiss target circle view */ + val dismissViewResId: Int, /** dimen resource id of the dismiss target circle view size */ @DimenRes val targetSizeResId: Int, /** dimen resource id of the icon size in the dismiss target */ @@ -121,6 +123,7 @@ class DismissView(context: Context) : FrameLayout(context) { setBackgroundDrawable(gradientDrawable) // Setup DismissCircleView + circle.id = config.dismissViewResId circle.setup(config.backgroundResId, config.iconResId, config.iconSizeResId) val targetSize: Int = resources.getDimensionPixelSize(config.targetSizeResId) circle.layoutParams = LayoutParams(targetSize, targetSize, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt index cc37bd3a4589..d45e1265daac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt @@ -112,7 +112,7 @@ abstract class RelativeTouchListener : View.OnTouchListener { viewPositionOnTouchDown.set(v.translationX, v.translationY) performedLongClick = false - v.handler.postDelayed({ + v.handler?.postDelayed({ if (v.isLongClickable) { performedLongClick = v.performLongClick() } @@ -122,7 +122,7 @@ abstract class RelativeTouchListener : View.OnTouchListener { MotionEvent.ACTION_MOVE -> { if (!movedEnough && hypot(dx, dy) > touchSlop && !performedLongClick) { movedEnough = true - v.handler.removeCallbacksAndMessages(null) + v.handler?.removeCallbacksAndMessages(null) } if (movedEnough) { @@ -138,7 +138,7 @@ abstract class RelativeTouchListener : View.OnTouchListener { } else if (!performedLongClick) { v.performClick() } else { - v.handler.removeCallbacksAndMessages(null) + v.handler?.removeCallbacksAndMessages(null) } velocityTracker.clear() @@ -146,7 +146,7 @@ abstract class RelativeTouchListener : View.OnTouchListener { } MotionEvent.ACTION_CANCEL -> { - v.handler.removeCallbacksAndMessages(null) + v.handler?.removeCallbacksAndMessages(null) velocityTracker.clear() movedEnough = false } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java index 3b32b6c7b083..d520ff791e07 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java @@ -126,12 +126,22 @@ public class PipBoundsState { private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback; private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>(); + // the size of the current bounds relative to the max size spec + private float mBoundsScale; + public PipBoundsState(@NonNull Context context, @NonNull SizeSpecSource sizeSpecSource, @NonNull PipDisplayLayoutState pipDisplayLayoutState) { mContext = context; reloadResources(); mSizeSpecSource = sizeSpecSource; mPipDisplayLayoutState = pipDisplayLayoutState; + + // Update the relative proportion of the bounds compared to max possible size. Max size + // spec takes the aspect ratio of the bounds into account, so both width and height + // scale by the same factor. + addPipExclusionBoundsChangeCallback((bounds) -> { + mBoundsScale = Math.min((float) bounds.width() / mMaxSize.x, 1.0f); + }); } /** Reloads the resources. */ @@ -160,6 +170,15 @@ public class PipBoundsState { return new Rect(mBounds); } + /** + * Get the scale of the current bounds relative to the maximum size possible. + * + * @return 1.0 if {@link PipBoundsState#getBounds()} equals {@link PipBoundsState#getMaxSize()}. + */ + public float getBoundsScale() { + return mBoundsScale; + } + /** Returns the current movement bounds. */ @NonNull public Rect getMovementBounds() { @@ -622,6 +641,9 @@ public class PipBoundsState { pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight); pw.println(innerPrefix + "mHasUserMovedPip=" + mHasUserMovedPip); pw.println(innerPrefix + "mHasUserResizedPip=" + mHasUserResizedPip); + pw.println(innerPrefix + "mMinSize=" + mMinSize); + pw.println(innerPrefix + "mMaxSize=" + mMaxSize); + pw.println(innerPrefix + "mBoundsScale" + mBoundsScale); if (mPipReentryState == null) { pw.println(innerPrefix + "mPipReentryState=null"); } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java index 1901e0bbe700..f9a286ec804f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java @@ -20,6 +20,15 @@ import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_RIGHT; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_30_70; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_70_30; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_MINIMIZE; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_NONE; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS; +import static com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition; + import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -28,6 +37,8 @@ import android.hardware.display.DisplayManager; import android.view.Display; import android.view.DisplayInfo; +import androidx.annotation.Nullable; + import java.util.ArrayList; /** @@ -195,6 +206,21 @@ public class DividerSnapAlgorithm { } } + /** + * Gets the SnapTarget corresponding to the given {@link SnapPosition}, or null if no such + * SnapTarget exists. + */ + @Nullable + public SnapTarget findSnapTarget(@SnapPosition int snapPosition) { + for (SnapTarget t : mTargets) { + if (t.snapPosition == snapPosition) { + return t; + } + } + + return null; + } + public float calculateDismissingFraction(int position) { if (position < mFirstSplitTarget.position) { return 1f - (float) (position - getStartInset()) @@ -263,7 +289,7 @@ public class DividerSnapAlgorithm { private SnapTarget snap(int position, boolean hardDismiss) { if (shouldApplyFreeSnapMode(position)) { - return new SnapTarget(position, position, SnapTarget.FLAG_NONE); + return new SnapTarget(position, position, SNAP_TO_NONE); } int minIndex = -1; float minDistance = Float.MAX_VALUE; @@ -291,8 +317,7 @@ public class DividerSnapAlgorithm { if (dockedSide == DOCKED_RIGHT) { startPos += mInsets.left; } - mTargets.add(new SnapTarget(startPos, startPos, SnapTarget.FLAG_DISMISS_START, - 0.35f)); + mTargets.add(new SnapTarget(startPos, startPos, SNAP_TO_START_AND_DISMISS, 0.35f)); switch (mSnapMode) { case SNAP_MODE_16_9: addRatio16_9Targets(isHorizontalDivision, dividerMax); @@ -307,15 +332,15 @@ public class DividerSnapAlgorithm { addMinimizedTarget(isHorizontalDivision, dockedSide); break; } - mTargets.add(new SnapTarget(dividerMax, dividerMax, SnapTarget.FLAG_DISMISS_END, 0.35f)); + mTargets.add(new SnapTarget(dividerMax, dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f)); } private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition, int bottomPosition, int dividerMax) { - maybeAddTarget(topPosition, topPosition - getStartInset()); + maybeAddTarget(topPosition, topPosition - getStartInset(), SNAP_TO_30_70); addMiddleTarget(isHorizontalDivision); maybeAddTarget(bottomPosition, - dividerMax - getEndInset() - (bottomPosition + mDividerSize)); + dividerMax - getEndInset() - (bottomPosition + mDividerSize), SNAP_TO_70_30); } private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) { @@ -349,16 +374,16 @@ public class DividerSnapAlgorithm { * Adds a target at {@param position} but only if the area with size of {@param smallerSize} * meets the minimal size requirement. */ - private void maybeAddTarget(int position, int smallerSize) { + private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapPosition) { if (smallerSize >= mMinimalSizeResizableTask) { - mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE)); + mTargets.add(new SnapTarget(position, position, snapPosition)); } } private void addMiddleTarget(boolean isHorizontalDivision) { int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, mInsets, mDisplayWidth, mDisplayHeight, mDividerSize); - mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE)); + mTargets.add(new SnapTarget(position, position, SNAP_TO_50_50)); } private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) { @@ -372,7 +397,7 @@ public class DividerSnapAlgorithm { position = mDisplayWidth - position - mInsets.right - mDividerSize; } } - mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE)); + mTargets.add(new SnapTarget(position, position, SNAP_TO_MINIMIZE)); } public SnapTarget getMiddleTarget() { @@ -412,6 +437,13 @@ public class DividerSnapAlgorithm { } /** + * Finds the {@link SnapPosition} nearest to the given position. + */ + public int calculateNearestSnapPosition(int currentPosition) { + return snap(currentPosition, /* hardDismiss */ true).snapPosition; + } + + /** * Cycles through all non-dismiss targets with a stepping of {@param increment}. It moves left * if {@param increment} is negative and moves right otherwise. */ @@ -435,14 +467,6 @@ public class DividerSnapAlgorithm { * Represents a snap target for the divider. */ public static class SnapTarget { - public static final int FLAG_NONE = 0; - - /** If the divider reaches this value, the left/top task should be dismissed. */ - public static final int FLAG_DISMISS_START = 1; - - /** If the divider reaches this value, the right/bottom task should be dismissed */ - public static final int FLAG_DISMISS_END = 2; - /** Position of this snap target. The right/bottom edge of the top/left task snaps here. */ public final int position; @@ -452,7 +476,10 @@ public class DividerSnapAlgorithm { */ public final int taskPosition; - public final int flag; + /** + * An int describing the placement of the divider in this snap target. + */ + public final @SnapPosition int snapPosition; public boolean isMiddleTarget; @@ -462,14 +489,15 @@ public class DividerSnapAlgorithm { */ private final float distanceMultiplier; - public SnapTarget(int position, int taskPosition, int flag) { - this(position, taskPosition, flag, 1f); + public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition) { + this(position, taskPosition, snapPosition, 1f); } - public SnapTarget(int position, int taskPosition, int flag, float distanceMultiplier) { + public SnapTarget(int position, int taskPosition, @SnapPosition int snapPosition, + float distanceMultiplier) { this.position = position; this.taskPosition = taskPosition; - this.flag = flag; + this.snapPosition = snapPosition; this.distanceMultiplier = distanceMultiplier; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 755dba0c895f..63cdb4f151ff 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -26,10 +26,10 @@ import static android.view.WindowManager.DOCKED_TOP; import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER; import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE; -import static com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END; -import static com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START; import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR; import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -67,6 +67,8 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.InteractionJankMonitorUtils; +import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; +import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import java.io.PrintWriter; @@ -116,7 +118,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange @VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm; private WindowContainerToken mWinToken1; private WindowContainerToken mWinToken2; - private int mDividePosition; + private int mDividerPosition; private boolean mInitialized = false; private boolean mFreezeDividerWindow = false; private int mOrientation; @@ -268,7 +270,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } int getDividePosition() { - return mDividePosition; + return mDividerPosition; + } + + /** + * Finds the {@link SnapPosition} nearest to the current divider position. + */ + public int calculateCurrentSnapPosition() { + return mDividerSnapAlgorithm.calculateNearestSnapPosition(mDividerPosition); } /** @@ -345,16 +354,16 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } private void initDividerPosition(Rect oldBounds) { - final float snapRatio = (float) mDividePosition + final float snapRatio = (float) mDividerPosition / (float) (isLandscape(oldBounds) ? oldBounds.width() : oldBounds.height()); // Estimate position by previous ratio. final float length = (float) (isLandscape() ? mRootBounds.width() : mRootBounds.height()); final int estimatePosition = (int) (length * snapRatio); // Init divider position by estimated position using current bounds snap algorithm. - mDividePosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget( + mDividerPosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget( estimatePosition).position; - updateBounds(mDividePosition); + updateBounds(mDividerPosition); } private void updateBounds(int position) { @@ -468,27 +477,29 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } void setDividePosition(int position, boolean applyLayoutChange) { - mDividePosition = position; - updateBounds(mDividePosition); + mDividerPosition = position; + updateBounds(mDividerPosition); if (applyLayoutChange) { mSplitLayoutHandler.onLayoutSizeChanged(this); } } /** Updates divide position and split bounds base on the ratio within root bounds. */ - public void setDivideRatio(float ratio) { - final int position = isLandscape() - ? mRootBounds.left + (int) (mRootBounds.width() * ratio) - : mRootBounds.top + (int) (mRootBounds.height() * ratio); - final DividerSnapAlgorithm.SnapTarget snapTarget = - mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position); + public void setDivideRatio(@PersistentSnapPosition int snapPosition) { + final DividerSnapAlgorithm.SnapTarget snapTarget = mDividerSnapAlgorithm.findSnapTarget( + snapPosition); + + if (snapTarget == null) { + throw new IllegalArgumentException("No SnapTarget for position " + snapPosition); + } + setDividePosition(snapTarget.position, false /* applyLayoutChange */); } /** Resets divider position. */ public void resetDividerPosition() { - mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position; - updateBounds(mDividePosition); + mDividerPosition = mDividerSnapAlgorithm.getMiddleTarget().position; + updateBounds(mDividerPosition); mWinToken1 = null; mWinToken2 = null; mWinBounds1.setEmpty(); @@ -511,13 +522,13 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * target indicates dismissing split. */ public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) { - switch (snapTarget.flag) { - case FLAG_DISMISS_START: + switch (snapTarget.snapPosition) { + case SNAP_TO_START_AND_DISMISS: flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION, () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */, EXIT_REASON_DRAG_DIVIDER)); break; - case FLAG_DISMISS_END: + case SNAP_TO_END_AND_DISMISS: flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION, () -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */, EXIT_REASON_DRAG_DIVIDER)); @@ -669,8 +680,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange @Override public void onAnimationEnd(Animator animation) { - mDividePosition = dividerPos; - updateBounds(mDividePosition); + mDividerPosition = dividerPos; + updateBounds(mDividerPosition); finishCallback.accept(insets); InteractionJankMonitorUtils.endTracing(CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java index be1b9b1227de..e73430056c89 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java @@ -26,6 +26,16 @@ import android.annotation.IntDef; /** Helper utility class of methods and constants that are available to be imported in Launcher. */ public class SplitScreenConstants { + /** + * Duration used for every split fade-in or fade-out. + */ + public static final int FADE_DURATION = 133; + + /////////////// + // IMPORTANT for the following SPLIT_POSITION and SNAP_TO constants: + // These int values must not be changed -- they are persisted to user-defined app pairs, and + // will break things if changed. + // /** * Split position isn't specified normally meaning to use what ever it is currently set to. @@ -44,11 +54,6 @@ public class SplitScreenConstants { */ public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1; - /** - * Duration used for every split fade-in or fade-out. - */ - public static final int FADE_DURATION = 133; - @IntDef(prefix = {"SPLIT_POSITION_"}, value = { SPLIT_POSITION_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT, @@ -57,6 +62,61 @@ public class SplitScreenConstants { public @interface SplitPosition { } + /** A snap target in the first half of the screen, where the split is roughly 30-70. */ + public static final int SNAP_TO_30_70 = 0; + + /** The 50-50 snap target */ + public static final int SNAP_TO_50_50 = 1; + + /** A snap target in the latter half of the screen, where the split is roughly 70-30. */ + public static final int SNAP_TO_70_30 = 2; + + /** + * These snap targets are used for split pairs in a stable, non-transient state. They may be + * persisted in Launcher when the user saves an app pair. They are a subset of + * {@link SnapPosition}. + */ + @IntDef(prefix = { "SNAP_TO_" }, value = { + SNAP_TO_30_70, + SNAP_TO_50_50, + SNAP_TO_70_30 + }) + public @interface PersistentSnapPosition {} + + /** + * Checks if the snapPosition in question is a {@link PersistentSnapPosition}. + */ + public static boolean isPersistentSnapPosition(@SnapPosition int snapPosition) { + return snapPosition == SNAP_TO_30_70 + || snapPosition == SNAP_TO_50_50 + || snapPosition == SNAP_TO_70_30; + } + + /** The divider doesn't snap to any target and is freely placeable. */ + public static final int SNAP_TO_NONE = 10; + + /** If the divider reaches this value, the left/top task should be dismissed. */ + public static final int SNAP_TO_START_AND_DISMISS = 11; + + /** If the divider reaches this value, the right/bottom task should be dismissed. */ + public static final int SNAP_TO_END_AND_DISMISS = 12; + + /** A snap target positioned near the screen edge for a minimized task */ + public static final int SNAP_TO_MINIMIZE = 13; + + @IntDef(prefix = { "SNAP_TO_" }, value = { + SNAP_TO_30_70, + SNAP_TO_50_50, + SNAP_TO_70_30, + SNAP_TO_NONE, + SNAP_TO_START_AND_DISMISS, + SNAP_TO_END_AND_DISMISS, + SNAP_TO_MINIMIZE + }) + public @interface SnapPosition {} + + /////////////// + public static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD}; public static final int[] CONTROLLED_WINDOWING_MODES = {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED}; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java index 9facbd542e6c..b52a118c7f1e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java @@ -49,11 +49,11 @@ import dagger.Provides; import java.util.Optional; /** - * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only - * accessible from components within the WM subcomponent (can be explicitly exposed to the - * SysUIComponent, see {@link WMComponent}). + * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only accessible + * from components within the WM subcomponent (can be explicitly exposed to the SysUIComponent, see + * {@link com.android.systemui.dagger.WMComponent}). * - * This module only defines Shell dependencies for the TV SystemUI implementation. Common + * <p>This module only defines Shell dependencies for the TV SystemUI implementation. Common * dependencies should go into {@link WMShellBaseModule}. */ @Module(includes = {TvPipModule.class}) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 45869e177c6d..ea7b2e92fefb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.SystemProperties; import android.view.IWindowManager; import android.view.accessibility.AccessibilityManager; +import android.window.SystemPerformanceHinter; import com.android.internal.logging.UiEventLogger; import com.android.launcher3.icons.IconProvider; @@ -38,6 +39,7 @@ import com.android.wm.shell.activityembedding.ActivityEmbeddingController; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.back.BackAnimationBackground; import com.android.wm.shell.back.BackAnimationController; +import com.android.wm.shell.back.ShellBackAnimationRegistry; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.common.DevicePostureController; @@ -71,7 +73,6 @@ import com.android.wm.shell.compatui.CompatUIConfiguration; import com.android.wm.shell.compatui.CompatUIController; import com.android.wm.shell.compatui.CompatUIShellCommandHandler; import com.android.wm.shell.desktopmode.DesktopMode; -import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.desktopmode.DesktopTasksController; @@ -85,6 +86,7 @@ import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedController; +import com.android.wm.shell.performance.PerfHintController; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; @@ -118,9 +120,9 @@ import java.util.Optional; /** * Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only * accessible from components within the WM subcomponent (can be explicitly exposed to the - * SysUIComponent, see {@link WMComponent}). + * SysUIComponent, see {@link com.android.systemui.dagger.WMComponent}). * - * This module only defines *common* dependencies across various SystemUI implementations, + * <p>This module only defines *common* dependencies across various SystemUI implementations, * dependencies that are device/form factor SystemUI implementation specific should go into their * respective modules (ie. {@link WMShellModule} for handheld, {@link TvWMShellModule} for tv, etc.) */ @@ -296,6 +298,17 @@ public abstract class WMShellBaseModule { return new LaunchAdjacentController(syncQueue); } + @WMSingleton + @Provides + static SystemPerformanceHinter provideSystemPerformanceHinter(Context context, + ShellInit shellInit, + ShellCommandHandler shellCommandHandler, + RootTaskDisplayAreaOrganizer rootTdaOrganizer) { + final PerfHintController perfHintController = + new PerfHintController(context, shellInit, shellCommandHandler, rootTdaOrganizer); + return perfHintController.getHinter(); + } + // // Back animation // @@ -322,16 +335,26 @@ public abstract class WMShellBaseModule { ShellController shellController, @ShellMainThread ShellExecutor shellExecutor, @ShellBackgroundThread Handler backgroundHandler, - BackAnimationBackground backAnimationBackground - ) { + BackAnimationBackground backAnimationBackground, + Optional<ShellBackAnimationRegistry> shellBackAnimationRegistry) { if (BackAnimationController.IS_ENABLED) { - return Optional.of( - new BackAnimationController(shellInit, shellController, shellExecutor, - backgroundHandler, context, backAnimationBackground)); + return shellBackAnimationRegistry.map( + (animations) -> + new BackAnimationController( + shellInit, + shellController, + shellExecutor, + backgroundHandler, + context, + backAnimationBackground, + animations)); } return Optional.empty(); } + @BindsOptionalOf + abstract ShellBackAnimationRegistry optionalBackAnimationRegistry(); + // // PiP (optional feature) // @@ -582,6 +605,7 @@ public abstract class WMShellBaseModule { @Provides static Transitions provideTransitions(Context context, ShellInit shellInit, + ShellCommandHandler shellCommandHandler, ShellController shellController, ShellTaskOrganizer organizer, TransactionPool pool, @@ -589,14 +613,13 @@ public abstract class WMShellBaseModule { @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler, @ShellAnimationThread ShellExecutor animExecutor, - ShellCommandHandler shellCommandHandler, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) { // TODO(b/238217847): Force override shell init if registration is disabled shellInit = new ShellInit(mainExecutor); } - return new Transitions(context, shellInit, shellController, organizer, pool, - displayController, mainExecutor, mainHandler, animExecutor, shellCommandHandler, + return new Transitions(context, shellInit, shellCommandHandler, shellController, organizer, + pool, displayController, mainExecutor, mainHandler, animExecutor, rootTaskDisplayAreaOrganizer); } @@ -635,15 +658,15 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static RootTaskDisplayAreaOrganizer provideRootTaskDisplayAreaOrganizer( - @ShellMainThread ShellExecutor mainExecutor, Context context) { - return new RootTaskDisplayAreaOrganizer(mainExecutor, context); + @ShellMainThread ShellExecutor mainExecutor, Context context, ShellInit shellInit) { + return new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit); } @WMSingleton @Provides static RootDisplayAreaOrganizer provideRootDisplayAreaOrganizer( - @ShellMainThread ShellExecutor mainExecutor) { - return new RootDisplayAreaOrganizer(mainExecutor); + @ShellMainThread ShellExecutor mainExecutor, ShellInit shellInit) { + return new RootDisplayAreaOrganizer(mainExecutor, shellInit); } @WMSingleton @@ -789,30 +812,10 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static Optional<DesktopMode> provideDesktopMode( - Optional<DesktopModeController> desktopModeController, Optional<DesktopTasksController> desktopTasksController) { - if (DesktopModeStatus.isProto2Enabled()) { - return desktopTasksController.map(DesktopTasksController::asDesktopMode); - } - return desktopModeController.map(DesktopModeController::asDesktopMode); + return desktopTasksController.map(DesktopTasksController::asDesktopMode); } - @BindsOptionalOf - @DynamicOverride - abstract DesktopModeController optionalDesktopModeController(); - - @WMSingleton - @Provides - static Optional<DesktopModeController> provideDesktopModeController( - @DynamicOverride Optional<Lazy<DesktopModeController>> desktopModeController) { - // Use optional-of-lazy for the dependency that this provider relies on. - // Lazy ensures that this provider will not be the cause the dependency is created - // when it will not be returned due to the condition below. - if (DesktopModeStatus.isProto1Enabled()) { - return desktopModeController.map(Lazy::get); - } - return Optional.empty(); - } @BindsOptionalOf @DynamicOverride @@ -825,7 +828,7 @@ public abstract class WMShellBaseModule { // Use optional-of-lazy for the dependency that this provider relies on. // Lazy ensures that this provider will not be the cause the dependency is created // when it will not be returned due to the condition below. - if (DesktopModeStatus.isProto2Enabled()) { + if (DesktopModeStatus.isEnabled()) { return desktopTasksController.map(Lazy::get); } return Optional.empty(); @@ -842,7 +845,7 @@ public abstract class WMShellBaseModule { // Use optional-of-lazy for the dependency that this provider relies on. // Lazy ensures that this provider will not be the cause the dependency is created // when it will not be returned due to the condition below. - if (DesktopModeStatus.isAnyEnabled()) { + if (DesktopModeStatus.isEnabled()) { return desktopModeTaskRepository.map(Lazy::get); } return Optional.empty(); 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 065e7b09a05d..a533ca5fa8fb 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 @@ -32,9 +32,11 @@ import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; +import com.android.wm.shell.activityembedding.ActivityEmbeddingController; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; +import com.android.wm.shell.bubbles.BubbleEducationController; import com.android.wm.shell.bubbles.BubbleLogger; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.properties.ProdBubbleProperties; @@ -52,8 +54,8 @@ import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellAnimationThread; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.dagger.back.ShellBackAnimationModule; import com.android.wm.shell.dagger.pip.PipModule; -import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.desktopmode.DesktopTasksController; @@ -100,17 +102,19 @@ import java.util.List; import java.util.Optional; /** - * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only - * accessible from components within the WM subcomponent (can be explicitly exposed to the - * SysUIComponent, see {@link WMComponent}). + * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only accessible + * from components within the WM subcomponent (can be explicitly exposed to the SysUIComponent, see + * {@link WMComponent}). * - * This module only defines Shell dependencies for handheld SystemUI implementation. Common + * <p>This module only defines Shell dependencies for handheld SystemUI implementation. Common * dependencies should go into {@link WMShellBaseModule}. */ -@Module(includes = { - WMShellBaseModule.class, - PipModule.class -}) +@Module( + includes = { + WMShellBaseModule.class, + PipModule.class, + ShellBackAnimationModule.class, + }) public abstract class WMShellModule { // @@ -132,11 +136,18 @@ public abstract class WMShellModule { @WMSingleton @Provides + static BubbleEducationController provideBubbleEducationProvider(Context context) { + return new BubbleEducationController(context); + } + + @WMSingleton + @Provides static BubbleData provideBubbleData(Context context, BubbleLogger logger, BubblePositioner positioner, + BubbleEducationController educationController, @ShellMainThread ShellExecutor mainExecutor) { - return new BubbleData(context, logger, positioner, mainExecutor); + return new BubbleData(context, logger, positioner, educationController, mainExecutor); } // Note: Handler needed for LauncherApps.register @@ -164,6 +175,7 @@ public abstract class WMShellModule { @ShellMainThread Handler mainHandler, @ShellBackgroundThread ShellExecutor bgExecutor, TaskViewTransitions taskViewTransitions, + Transitions transitions, SyncTransactionQueue syncQueue, IWindowManager wmService) { return new BubbleController(context, shellInit, shellCommandHandler, shellController, data, @@ -173,7 +185,8 @@ public abstract class WMShellModule { statusBarService, windowManager, windowManagerShellWrapper, userManager, launcherApps, logger, taskStackListener, organizer, positioner, displayController, oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor, - taskViewTransitions, syncQueue, wmService, ProdBubbleProperties.INSTANCE); + taskViewTransitions, transitions, syncQueue, wmService, + ProdBubbleProperties.INSTANCE); } // @@ -187,26 +200,32 @@ public abstract class WMShellModule { @ShellMainThread Handler mainHandler, @ShellMainThread Choreographer mainChoreographer, ShellInit shellInit, + ShellCommandHandler shellCommandHandler, ShellTaskOrganizer taskOrganizer, DisplayController displayController, ShellController shellController, + DisplayInsetsController displayInsetsController, SyncTransactionQueue syncQueue, Transitions transitions, - Optional<DesktopModeController> desktopModeController, - Optional<DesktopTasksController> desktopTasksController) { - if (DesktopModeStatus.isAnyEnabled()) { + Optional<DesktopTasksController> desktopTasksController, + RecentsTransitionHandler recentsTransitionHandler, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { + if (DesktopModeStatus.isEnabled()) { return new DesktopModeWindowDecorViewModel( context, mainHandler, mainChoreographer, shellInit, + shellCommandHandler, taskOrganizer, displayController, shellController, + displayInsetsController, syncQueue, transitions, - desktopModeController, - desktopTasksController); + desktopTasksController, + recentsTransitionHandler, + rootTaskDisplayAreaOrganizer); } return new CaptionWindowDecorViewModel( context, @@ -346,14 +365,14 @@ public abstract class WMShellModule { @Nullable PipTransitionController pipTransitionController, Optional<RecentsTransitionHandler> recentsTransitionHandler, KeyguardTransitionHandler keyguardTransitionHandler, - Optional<DesktopModeController> desktopModeController, Optional<DesktopTasksController> desktopTasksController, Optional<UnfoldTransitionHandler> unfoldHandler, + Optional<ActivityEmbeddingController> activityEmbeddingController, Transitions transitions) { return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional, pipTransitionController, recentsTransitionHandler, - keyguardTransitionHandler, desktopModeController, desktopTasksController, - unfoldHandler); + keyguardTransitionHandler, desktopTasksController, + unfoldHandler, activityEmbeddingController); } @WMSingleton @@ -464,24 +483,6 @@ public abstract class WMShellModule { @WMSingleton @Provides @DynamicOverride - static DesktopModeController provideDesktopModeController(Context context, - ShellInit shellInit, - ShellController shellController, - ShellTaskOrganizer shellTaskOrganizer, - RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, - Transitions transitions, - @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository, - @ShellMainThread Handler mainHandler, - @ShellMainThread ShellExecutor mainExecutor - ) { - return new DesktopModeController(context, shellInit, shellController, shellTaskOrganizer, - rootTaskDisplayAreaOrganizer, transitions, desktopModeTaskRepository, mainHandler, - mainExecutor); - } - - @WMSingleton - @Provides - @DynamicOverride static DesktopTasksController provideDesktopTasksController( Context context, ShellInit shellInit, @@ -497,13 +498,14 @@ public abstract class WMShellModule { ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler, @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository, LaunchAdjacentController launchAdjacentController, + RecentsTransitionHandler recentsTransitionHandler, @ShellMainThread ShellExecutor mainExecutor ) { return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController, displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler, toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository, - launchAdjacentController, mainExecutor); + launchAdjacentController, recentsTransitionHandler, mainExecutor); } @WMSingleton @@ -546,8 +548,7 @@ public abstract class WMShellModule { @ShellCreateTriggerOverride @Provides static Object provideIndependentShellComponentsToCreate( - DefaultMixedHandler defaultMixedHandler, - Optional<DesktopModeController> desktopModeController) { + DefaultMixedHandler defaultMixedHandler) { return new Object(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java new file mode 100644 index 000000000000..b34c6b213df4 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.dagger.back; + +import com.android.wm.shell.back.CrossActivityAnimation; +import com.android.wm.shell.back.CrossTaskBackAnimation; +import com.android.wm.shell.back.CustomizeActivityAnimation; +import com.android.wm.shell.back.ShellBackAnimation; +import com.android.wm.shell.back.ShellBackAnimationRegistry; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; + +/** Default animation definitions for predictive back. */ +@Module +public interface ShellBackAnimationModule { + /** Default animation registry */ + @Provides + static ShellBackAnimationRegistry provideBackAnimationRegistry( + @ShellBackAnimation.CrossActivity ShellBackAnimation crossActivity, + @ShellBackAnimation.CrossTask ShellBackAnimation crossTask, + @ShellBackAnimation.CustomizeActivity ShellBackAnimation customizeActivity) { + return new ShellBackAnimationRegistry( + crossActivity, + crossTask, + customizeActivity, + /* defaultBackToHomeAnimation= */ null); + } + + /** Default cross activity back animation */ + @Binds + @ShellBackAnimation.CrossActivity + ShellBackAnimation bindCrossActivityShellBackAnimation( + CrossActivityAnimation crossActivityAnimation); + + /** Default cross task back animation */ + @Binds + @ShellBackAnimation.CrossTask + ShellBackAnimation provideCrossTaskShellBackAnimation( + CrossTaskBackAnimation crossTaskBackAnimation); + + /** Default customized activity back animation */ + @Binds + @ShellBackAnimation.CustomizeActivity + ShellBackAnimation provideCustomizeActivityShellBackAnimation( + CustomizeActivityAnimation customizeActivityAnimation); +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/OWNERS new file mode 100644 index 000000000000..74a29ddad073 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/OWNERS @@ -0,0 +1 @@ +hwwang@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java index af97cf68915f..1898ea737729 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java @@ -17,19 +17,28 @@ package com.android.wm.shell.dagger.pip; import android.annotation.NonNull; +import android.content.Context; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.common.pip.PipDisplayLayoutState; +import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.dagger.WMShellBaseModule; import com.android.wm.shell.dagger.WMSingleton; -import com.android.wm.shell.pip2.PipTransition; +import com.android.wm.shell.pip2.phone.PipController; +import com.android.wm.shell.pip2.phone.PipTransition; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import dagger.Module; import dagger.Provides; +import java.util.Optional; + /** * Provides dependencies from {@link com.android.wm.shell.pip2}, this implementation is meant to be * the successor of its sibling {@link Pip1Module}. @@ -42,8 +51,26 @@ public abstract class Pip2Module { @NonNull ShellTaskOrganizer shellTaskOrganizer, @NonNull Transitions transitions, PipBoundsState pipBoundsState, - PipBoundsAlgorithm pipBoundsAlgorithm) { + PipBoundsAlgorithm pipBoundsAlgorithm, + Optional<PipController> pipController) { return new PipTransition(shellInit, shellTaskOrganizer, transitions, pipBoundsState, null, pipBoundsAlgorithm); } + + @WMSingleton + @Provides + static Optional<PipController> providePipController(Context context, + ShellInit shellInit, + ShellController shellController, + DisplayController displayController, + DisplayInsetsController displayInsetsController, + PipDisplayLayoutState pipDisplayLayoutState) { + if (!PipUtils.isPip2ExperimentEnabled()) { + return Optional.empty(); + } else { + return Optional.ofNullable(PipController.create( + context, shellInit, shellController, displayController, displayInsetsController, + pipDisplayLayoutState)); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java index 570f0a3db35a..f2631eff890d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java @@ -19,6 +19,7 @@ package com.android.wm.shell.dagger.pip; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.dagger.WMSingleton; import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.pip2.phone.PipTransition; import dagger.Module; import dagger.Provides; @@ -36,7 +37,7 @@ public abstract class PipModule { @Provides static PipTransitionController providePipTransitionController( com.android.wm.shell.pip.PipTransition legacyPipTransition, - com.android.wm.shell.pip2.PipTransition newPipTransition) { + PipTransition newPipTransition) { if (PipUtils.isPip2ExperimentEnabled()) { return newPipTransition; } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java deleted file mode 100644 index 5b24d7a60c4e..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ /dev/null @@ -1,557 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.desktopmode; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_NONE; -import static android.view.WindowManager.TRANSIT_OPEN; -import static android.view.WindowManager.TRANSIT_TO_FRONT; - -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; -import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE; - -import android.app.ActivityManager.RunningTaskInfo; -import android.app.WindowConfiguration; -import android.content.Context; -import android.content.res.TypedArray; -import android.database.ContentObserver; -import android.graphics.Region; -import android.net.Uri; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.UserHandle; -import android.provider.Settings; -import android.util.ArraySet; -import android.view.SurfaceControl; -import android.view.WindowManager; -import android.window.DisplayAreaInfo; -import android.window.TransitionInfo; -import android.window.TransitionRequestInfo; -import android.window.WindowContainerTransaction; - -import androidx.annotation.BinderThread; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.ExternalInterfaceBinder; -import com.android.wm.shell.common.RemoteCallable; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.annotations.ExternalThread; -import com.android.wm.shell.common.annotations.ShellMainThread; -import com.android.wm.shell.sysui.ShellController; -import com.android.wm.shell.sysui.ShellInit; -import com.android.wm.shell.transition.Transitions; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -/** - * Handles windowing changes when desktop mode system setting changes - */ -public class DesktopModeController implements RemoteCallable<DesktopModeController>, - Transitions.TransitionHandler { - - private final Context mContext; - private final ShellController mShellController; - private final ShellTaskOrganizer mShellTaskOrganizer; - private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; - private final Transitions mTransitions; - private final DesktopModeTaskRepository mDesktopModeTaskRepository; - private final ShellExecutor mMainExecutor; - private final DesktopModeImpl mDesktopModeImpl = new DesktopModeImpl(); - private final SettingsObserver mSettingsObserver; - - public DesktopModeController(Context context, - ShellInit shellInit, - ShellController shellController, - ShellTaskOrganizer shellTaskOrganizer, - RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, - Transitions transitions, - DesktopModeTaskRepository desktopModeTaskRepository, - @ShellMainThread Handler mainHandler, - @ShellMainThread ShellExecutor mainExecutor) { - mContext = context; - mShellController = shellController; - mShellTaskOrganizer = shellTaskOrganizer; - mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; - mTransitions = transitions; - mDesktopModeTaskRepository = desktopModeTaskRepository; - mMainExecutor = mainExecutor; - mSettingsObserver = new SettingsObserver(mContext, mainHandler); - if (DesktopModeStatus.isProto1Enabled()) { - shellInit.addInitCallback(this::onInit, this); - } - } - - private void onInit() { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController"); - mShellController.addExternalInterface(KEY_EXTRA_SHELL_DESKTOP_MODE, - this::createExternalInterface, this); - mSettingsObserver.observe(); - if (DesktopModeStatus.isActive(mContext)) { - updateDesktopModeActive(true); - } - mTransitions.addHandler(this); - } - - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mMainExecutor; - } - - /** - * Get connection interface between sysui and shell - */ - public DesktopMode asDesktopMode() { - return mDesktopModeImpl; - } - - /** - * Creates a new instance of the external interface to pass to another process. - */ - private ExternalInterfaceBinder createExternalInterface() { - return new IDesktopModeImpl(this); - } - - /** - * Adds a listener to find out about changes in the visibility of freeform tasks. - * - * @param listener the listener to add. - * @param callbackExecutor the executor to call the listener on. - */ - public void addVisibleTasksListener(DesktopModeTaskRepository.VisibleTasksListener listener, - Executor callbackExecutor) { - mDesktopModeTaskRepository.addVisibleTasksListener(listener, callbackExecutor); - } - - /** - * Adds a listener to track changes to corners of desktop mode tasks. - * @param listener the listener to add. - * @param callbackExecutor the executor to call the listener on. - */ - public void addTaskCornerListener(Consumer<Region> listener, - Executor callbackExecutor) { - mDesktopModeTaskRepository.setTaskCornerListener(listener, callbackExecutor); - } - - @VisibleForTesting - void updateDesktopModeActive(boolean active) { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeActive: active=%s", active); - - int displayId = mContext.getDisplayId(); - - ArrayList<RunningTaskInfo> runningTasks = mShellTaskOrganizer.getRunningTasks(displayId); - - WindowContainerTransaction wct = new WindowContainerTransaction(); - // Reset freeform windowing mode that is set per task level so tasks inherit it - clearFreeformForStandardTasks(runningTasks, wct); - if (active) { - moveHomeBehindVisibleTasks(runningTasks, wct); - setDisplayAreaWindowingMode(displayId, WINDOWING_MODE_FREEFORM, wct); - } else { - clearBoundsForStandardTasks(runningTasks, wct); - setDisplayAreaWindowingMode(displayId, WINDOWING_MODE_FULLSCREEN, wct); - } - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mTransitions.startTransition(TRANSIT_CHANGE, wct, null); - } else { - mRootTaskDisplayAreaOrganizer.applyTransaction(wct); - } - } - - private WindowContainerTransaction clearBoundsForStandardTasks( - ArrayList<RunningTaskInfo> runningTasks, WindowContainerTransaction wct) { - ProtoLog.v(WM_SHELL_DESKTOP_MODE, "prepareClearBoundsForTasks"); - for (RunningTaskInfo taskInfo : runningTasks) { - if (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) { - ProtoLog.v(WM_SHELL_DESKTOP_MODE, "clearing bounds for token=%s taskInfo=%s", - taskInfo.token, taskInfo); - wct.setBounds(taskInfo.token, null); - } - } - return wct; - } - - private void clearFreeformForStandardTasks(ArrayList<RunningTaskInfo> runningTasks, - WindowContainerTransaction wct) { - ProtoLog.v(WM_SHELL_DESKTOP_MODE, "prepareClearFreeformForTasks"); - for (RunningTaskInfo taskInfo : runningTasks) { - if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM - && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) { - ProtoLog.v(WM_SHELL_DESKTOP_MODE, - "clearing windowing mode for token=%s taskInfo=%s", taskInfo.token, - taskInfo); - wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); - } - } - } - - private void moveHomeBehindVisibleTasks(ArrayList<RunningTaskInfo> runningTasks, - WindowContainerTransaction wct) { - ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks"); - RunningTaskInfo homeTask = null; - ArrayList<RunningTaskInfo> visibleTasks = new ArrayList<>(); - for (RunningTaskInfo taskInfo : runningTasks) { - if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) { - homeTask = taskInfo; - } else if (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD - && taskInfo.isVisible()) { - visibleTasks.add(taskInfo); - } - } - if (homeTask == null) { - ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks: home task not found"); - } else { - ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks: visible tasks %d", - visibleTasks.size()); - wct.reorder(homeTask.getToken(), true /* onTop */); - for (RunningTaskInfo task : visibleTasks) { - wct.reorder(task.getToken(), true /* onTop */); - } - } - } - - private void setDisplayAreaWindowingMode(int displayId, - @WindowConfiguration.WindowingMode int windowingMode, WindowContainerTransaction wct) { - DisplayAreaInfo displayAreaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo( - displayId); - if (displayAreaInfo == null) { - ProtoLog.e(WM_SHELL_DESKTOP_MODE, - "unable to update windowing mode for display %d display not found", displayId); - return; - } - - ProtoLog.v(WM_SHELL_DESKTOP_MODE, - "setWindowingMode: displayId=%d current wmMode=%d new wmMode=%d", displayId, - displayAreaInfo.configuration.windowConfiguration.getWindowingMode(), - windowingMode); - - wct.setWindowingMode(displayAreaInfo.token, windowingMode); - } - - /** - * Show apps on desktop - */ - void showDesktopApps(int displayId) { - // Bring apps to front, ignoring their visibility status to always ensure they are on top. - WindowContainerTransaction wct = new WindowContainerTransaction(); - bringDesktopAppsToFront(displayId, wct); - - if (!wct.isEmpty()) { - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - // TODO(b/268662477): add animation for the transition - mTransitions.startTransition(TRANSIT_NONE, wct, null /* handler */); - } else { - mShellTaskOrganizer.applyTransaction(wct); - } - } - } - - /** Get number of tasks that are marked as visible */ - int getVisibleTaskCount(int displayId) { - return mDesktopModeTaskRepository.getVisibleTaskCount(displayId); - } - - private void bringDesktopAppsToFront(int displayId, WindowContainerTransaction wct) { - final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(displayId); - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size()); - - final List<RunningTaskInfo> taskInfos = new ArrayList<>(); - for (Integer taskId : activeTasks) { - RunningTaskInfo taskInfo = mShellTaskOrganizer.getRunningTaskInfo(taskId); - if (taskInfo != null) { - taskInfos.add(taskInfo); - } - } - - if (taskInfos.isEmpty()) { - return; - } - - moveHomeTaskToFront(wct); - - ProtoLog.d(WM_SHELL_DESKTOP_MODE, - "bringDesktopAppsToFront: reordering all active tasks to the front"); - final List<Integer> allTasksInZOrder = - mDesktopModeTaskRepository.getFreeformTasksInZOrder(); - // Sort by z-order, bottom to top, so that the top-most task is reordered to the top last - // in the WCT. - taskInfos.sort(Comparator.comparingInt(task -> -allTasksInZOrder.indexOf(task.taskId))); - for (RunningTaskInfo task : taskInfos) { - wct.reorder(task.token, true); - } - } - - private void moveHomeTaskToFront(WindowContainerTransaction wct) { - for (RunningTaskInfo task : mShellTaskOrganizer.getRunningTasks(mContext.getDisplayId())) { - if (task.getActivityType() == ACTIVITY_TYPE_HOME) { - wct.reorder(task.token, true /* onTop */); - return; - } - } - } - - /** - * Update corner rects stored for a specific task - * @param taskId task to update - * @param taskCorners task's new corner handles - */ - public void onTaskCornersChanged(int taskId, Region taskCorners) { - mDesktopModeTaskRepository.updateTaskCorners(taskId, taskCorners); - } - - /** - * Remove corners saved for a task. Likely used due to task closure. - * @param taskId task to remove - */ - public void removeCornersForTask(int taskId) { - mDesktopModeTaskRepository.removeTaskCorners(taskId); - } - - /** - * Moves a specifc task to the front. - * @param taskInfo the task to show in front. - */ - public void moveTaskToFront(RunningTaskInfo taskInfo) { - WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.reorder(taskInfo.token, true /* onTop */); - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mTransitions.startTransition(TRANSIT_TO_FRONT, wct, null); - } else { - mShellTaskOrganizer.applyTransaction(wct); - } - } - - /** - * Turn desktop mode on or off - * @param active the desired state for desktop mode setting - */ - public void setDesktopModeActive(boolean active) { - int value = active ? 1 : 0; - Settings.System.putInt(mContext.getContentResolver(), Settings.System.DESKTOP_MODE, value); - } - - /** - * Returns the windowing mode of the display area with the specified displayId. - * @param displayId - * @return - */ - public int getDisplayAreaWindowingMode(int displayId) { - return mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId) - .configuration.windowConfiguration.getWindowingMode(); - } - - @Override - public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - // This handler should never be the sole handler, so should not animate anything. - return false; - } - - @Nullable - @Override - public WindowContainerTransaction handleRequest(@NonNull IBinder transition, - @NonNull TransitionRequestInfo request) { - RunningTaskInfo triggerTask = request.getTriggerTask(); - // Only do anything if we are in desktop mode and opening/moving-to-front a task/app in - // freeform - if (!DesktopModeStatus.isActive(mContext)) { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, - "skip shell transition request: desktop mode not active"); - return null; - } - if (request.getType() != TRANSIT_OPEN && request.getType() != TRANSIT_TO_FRONT) { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, - "skip shell transition request: unsupported type %s", - WindowManager.transitTypeToString(request.getType())); - return null; - } - if (triggerTask == null || triggerTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "skip shell transition request: not freeform task"); - return null; - } - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "handle shell transition request: %s", request); - - WindowContainerTransaction wct = new WindowContainerTransaction(); - bringDesktopAppsToFront(triggerTask.displayId, wct); - wct.reorder(triggerTask.token, true /* onTop */); - - return wct; - } - - /** - * Applies the proper surface states (rounded corners) to tasks when desktop mode is active. - * This is intended to be used when desktop mode is part of another animation but isn't, itself, - * animating. - */ - public void syncSurfaceState(@NonNull TransitionInfo info, - SurfaceControl.Transaction finishTransaction) { - // Add rounded corners to freeform windows - final TypedArray ta = mContext.obtainStyledAttributes( - new int[]{android.R.attr.dialogCornerRadius}); - final int cornerRadius = ta.getDimensionPixelSize(0, 0); - ta.recycle(); - for (TransitionInfo.Change change: info.getChanges()) { - if (change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FREEFORM) { - finishTransaction.setCornerRadius(change.getLeash(), cornerRadius); - } - } - } - - /** - * A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE} - */ - private final class SettingsObserver extends ContentObserver { - - private final Uri mDesktopModeSetting = Settings.System.getUriFor( - Settings.System.DESKTOP_MODE); - - private final Context mContext; - - SettingsObserver(Context context, Handler handler) { - super(handler); - mContext = context; - } - - public void observe() { - // TODO(b/242867463): listen for setting change for all users - mContext.getContentResolver().registerContentObserver(mDesktopModeSetting, - false /* notifyForDescendants */, this /* observer */, UserHandle.USER_CURRENT); - } - - @Override - public void onChange(boolean selfChange, @Nullable Uri uri) { - if (mDesktopModeSetting.equals(uri)) { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Received update for desktop mode setting"); - desktopModeSettingChanged(); - } - } - - private void desktopModeSettingChanged() { - boolean enabled = DesktopModeStatus.isActive(mContext); - updateDesktopModeActive(enabled); - } - } - - /** - * The interface for calls from outside the shell, within the host process. - */ - @ExternalThread - private final class DesktopModeImpl implements DesktopMode { - - @Override - public void addVisibleTasksListener( - DesktopModeTaskRepository.VisibleTasksListener listener, - Executor callbackExecutor) { - mMainExecutor.execute(() -> { - DesktopModeController.this.addVisibleTasksListener(listener, callbackExecutor); - }); - } - - @Override - public void addDesktopGestureExclusionRegionListener(Consumer<Region> listener, - Executor callbackExecutor) { - mMainExecutor.execute(() -> { - DesktopModeController.this.addTaskCornerListener(listener, callbackExecutor); - }); - } - } - - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class IDesktopModeImpl extends IDesktopMode.Stub - implements ExternalInterfaceBinder { - - private DesktopModeController mController; - - IDesktopModeImpl(DesktopModeController controller) { - mController = controller; - } - - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - @Override - public void invalidate() { - mController = null; - } - - @Override - public void showDesktopApps(int displayId) { - executeRemoteCallWithTaskPermission(mController, "showDesktopApps", - controller -> controller.showDesktopApps(displayId)); - } - - @Override - public void showDesktopApp(int taskId) throws RemoteException { - // TODO - } - - @Override - public int getVisibleTaskCount(int displayId) throws RemoteException { - int[] result = new int[1]; - executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount", - controller -> result[0] = controller.getVisibleTaskCount(displayId), - true /* blocking */ - ); - return result[0]; - } - - @Override - public void onDesktopSplitSelectAnimComplete(RunningTaskInfo taskInfo) { - - } - - @Override - public void stashDesktopApps(int displayId) throws RemoteException { - // Stashing of desktop apps not needed. Apps always launch on desktop - } - - @Override - public void hideStashedDesktopApps(int displayId) throws RemoteException { - // Stashing of desktop apps not needed. Apps always launch on desktop - } - - @Override - public void setTaskListener(IDesktopTaskListener listener) throws RemoteException { - // TODO(b/261234402): move visibility from sysui state to listener - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java index 517f9f2aba27..77831136b0bc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java @@ -16,14 +16,7 @@ package com.android.wm.shell.desktopmode; -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; - -import android.content.Context; import android.os.SystemProperties; -import android.os.UserHandle; -import android.provider.Settings; - -import com.android.internal.protolog.common.ProtoLog; /** * Constants for desktop mode feature @@ -31,13 +24,7 @@ import com.android.internal.protolog.common.ProtoLog; public class DesktopModeStatus { /** - * Flag to indicate whether desktop mode is available on the device - */ - private static final boolean IS_SUPPORTED = SystemProperties.getBoolean( - "persist.wm.debug.desktop_mode", false); - - /** - * Flag to indicate whether desktop mode proto 2 is available on the device + * Flag to indicate whether desktop mode proto is available on the device */ private static final boolean IS_PROTO2_ENABLED = SystemProperties.getBoolean( "persist.wm.debug.desktop_mode_2", false); @@ -64,28 +51,13 @@ public class DesktopModeStatus { "persist.wm.debug.desktop_stashing", false); /** - * Return {@code true} if desktop mode support is enabled - */ - public static boolean isProto1Enabled() { - return IS_SUPPORTED; - } - - /** * Return {@code true} is desktop windowing proto 2 is enabled */ - public static boolean isProto2Enabled() { + public static boolean isEnabled() { return IS_PROTO2_ENABLED; } /** - * Return {@code true} if proto 1 or 2 is enabled. - * Can be used to guard logic that is common for both prototypes. - */ - public static boolean isAnyEnabled() { - return isProto1Enabled() || isProto2Enabled(); - } - - /** * Return {@code true} if veiled resizing is active. If false, fluid resizing is used. */ public static boolean isVeiledResizeEnabled() { @@ -99,26 +71,4 @@ public class DesktopModeStatus { public static boolean isStashingEnabled() { return IS_STASHING_ENABLED; } - /** - * Check if desktop mode is active - * - * @return {@code true} if active - */ - public static boolean isActive(Context context) { - if (!isAnyEnabled()) { - return false; - } - if (isProto2Enabled()) { - // Desktop mode is always active in prototype 2 - return true; - } - try { - int result = Settings.System.getIntForUser(context.getContentResolver(), - Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT); - return result != 0; - } catch (Exception e) { - ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e); - return false; - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index c05af73e6765..c0fc02fadd4d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -52,8 +52,8 @@ class DesktopModeTaskRepository { private val activeTasksListeners = ArraySet<ActiveTasksListener>() // Track visible tasks separately because a task may be part of the desktop but not visible. private val visibleTasksListeners = ArrayMap<VisibleTasksListener, Executor>() - // Track corners of desktop tasks, used to determine gesture exclusion - private val desktopCorners = SparseArray<Region>() + // Track corner/caption regions of desktop tasks, used to determine gesture exclusion + private val desktopExclusionRegions = SparseArray<Region>() private var desktopGestureExclusionListener: Consumer<Region>? = null private var desktopGestureExclusionExecutor: Executor? = null @@ -96,10 +96,11 @@ class DesktopModeTaskRepository { } /** - * Add a Consumer which will inform other classes of changes to corners for all Desktop tasks. + * Add a Consumer which will inform other classes of changes to exclusion regions for all + * Desktop tasks. */ - fun setTaskCornerListener(cornersListener: Consumer<Region>, executor: Executor) { - desktopGestureExclusionListener = cornersListener + fun setExclusionRegionListener(regionListener: Consumer<Region>, executor: Executor) { + desktopGestureExclusionListener = regionListener desktopGestureExclusionExecutor = executor executor.execute { desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion()) @@ -107,14 +108,14 @@ class DesktopModeTaskRepository { } /** - * Create a new merged region representative of all corners in all desktop tasks. + * Create a new merged region representative of all exclusion regions in all desktop tasks. */ private fun calculateDesktopExclusionRegion(): Region { - val desktopCornersRegion = Region() - desktopCorners.valueIterator().forEach { taskCorners -> - desktopCornersRegion.op(taskCorners, Region.Op.UNION) + val desktopExclusionRegion = Region() + desktopExclusionRegions.valueIterator().forEach { taskExclusionRegion -> + desktopExclusionRegion.op(taskExclusionRegion, Region.Op.UNION) } - return desktopCornersRegion + return desktopExclusionRegion } /** @@ -294,22 +295,24 @@ class DesktopModeTaskRepository { } /** - * Updates the active desktop corners; if desktopCorners has been accepted by - * desktopCornersListener, it will be updated in the appropriate classes. + * Updates the active desktop gesture exclusion regions; if desktopExclusionRegions has been + * accepted by desktopGestureExclusionListener, it will be updated in the + * appropriate classes. */ - fun updateTaskCorners(taskId: Int, taskCorners: Region) { - desktopCorners.put(taskId, taskCorners) + fun updateTaskExclusionRegions(taskId: Int, taskExclusionRegions: Region) { + desktopExclusionRegions.put(taskId, taskExclusionRegions) desktopGestureExclusionExecutor?.execute { desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion()) } } /** - * Removes the active desktop corners for the specified task; if desktopCorners has been - * accepted by desktopCornersListener, it will be updated in the appropriate classes. + * Removes the desktop gesture exclusion region for the specified task; if exclusionRegion + * has been accepted by desktopGestureExclusionListener, it will be updated in the + * appropriate classes. */ - fun removeTaskCorners(taskId: Int) { - desktopCorners.delete(taskId) + fun removeExclusionRegion(taskId: Int) { + desktopExclusionRegions.delete(taskId) desktopGestureExclusionExecutor?.execute { desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion()) } 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 4e418e11d226..412a5b5a6997 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 @@ -16,9 +16,7 @@ package com.android.wm.shell.desktopmode -import android.R import android.app.ActivityManager.RunningTaskInfo -import android.app.WindowConfiguration import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM @@ -27,7 +25,6 @@ import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.app.WindowConfiguration.WindowingMode import android.content.Context -import android.content.res.TypedArray import android.graphics.Point import android.graphics.PointF import android.graphics.Rect @@ -40,10 +37,12 @@ import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_NONE import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_FRONT +import android.window.RemoteTransition import android.window.TransitionInfo import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction import androidx.annotation.BinderThread +import com.android.internal.policy.ScreenDecorationsUtils import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.DisplayController @@ -61,11 +60,15 @@ import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener import com.android.wm.shell.splitscreen.SplitScreenController +import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.sysui.ShellSharedConstants +import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions import com.android.wm.shell.util.KtProtoLog import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration @@ -91,6 +94,7 @@ class DesktopTasksController( ToggleResizeDesktopTaskTransitionHandler, private val desktopModeTaskRepository: DesktopModeTaskRepository, private val launchAdjacentController: LaunchAdjacentController, + private val recentsTransitionHandler: RecentsTransitionHandler, @ShellMainThread private val mainExecutor: ShellExecutor ) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler { @@ -109,18 +113,22 @@ class DesktopTasksController( private val transitionAreaHeight get() = context.resources.getDimensionPixelSize( - com.android.wm.shell.R.dimen.desktop_mode_transition_area_height) + com.android.wm.shell.R.dimen.desktop_mode_transition_area_height + ) private val transitionAreaWidth get() = context.resources.getDimensionPixelSize( - com.android.wm.shell.R.dimen.desktop_mode_transition_area_width) + com.android.wm.shell.R.dimen.desktop_mode_transition_area_width + ) + + private var recentsAnimationRunning = false // This is public to avoid cyclic dependency; it is set by SplitScreenController lateinit var splitScreenController: SplitScreenController init { desktopMode = DesktopModeImpl() - if (DesktopModeStatus.isProto2Enabled()) { + if (DesktopModeStatus.isEnabled()) { shellInit.addInitCallback({ onInit() }, this) } } @@ -135,23 +143,38 @@ class DesktopTasksController( ) transitions.addHandler(this) desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor) + + recentsTransitionHandler.addTransitionStateListener( + object : RecentsTransitionStateListener { + override fun onAnimationStateChanged(running: Boolean) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: recents animation state changed running=%b", + running + ) + recentsAnimationRunning = running + } + } + ) } /** Show all tasks, that are part of the desktop, on top of launcher */ - fun showDesktopApps(displayId: Int) { + fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) { KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps") val wct = WindowContainerTransaction() - // TODO(b/278084491): pass in display id bringDesktopAppsToFront(displayId, wct) - // Execute transaction if there are pending operations - if (!wct.isEmpty) { - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - // TODO(b/268662477): add animation for the transition - transitions.startTransition(TRANSIT_NONE, wct, null /* handler */) - } else { - shellTaskOrganizer.applyTransaction(wct) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + // TODO(b/255649902): ensure remote transition is supplied once state is introduced + val transitionType = if (remoteTransition == null) TRANSIT_NONE else TRANSIT_TO_FRONT + val handler = remoteTransition?.let { + OneShotRemoteHandler(transitions.mainExecutor, remoteTransition) + } + transitions.startTransition(transitionType, wct, handler).also { t -> + handler?.setTransition(t) } + } else { + shellTaskOrganizer.applyTransaction(wct) } } @@ -212,6 +235,7 @@ class DesktopTasksController( "DesktopTasksController: moveToDesktop taskId=%d", task.taskId ) + exitSplitIfApplicable(wct, task) // Bring other apps to front first bringDesktopAppsToFront(task.displayId, wct) addMoveToDesktopChanges(wct, task) @@ -239,6 +263,7 @@ class DesktopTasksController( taskInfo.taskId ) val wct = WindowContainerTransaction() + exitSplitIfApplicable(wct, taskInfo) moveHomeTaskToFront(wct) addMoveToDesktopChanges(wct, taskInfo) wct.setBounds(taskInfo.token, startBounds) @@ -318,7 +343,7 @@ class DesktopTasksController( task.taskId ) val wct = WindowContainerTransaction() - wct.setWindowingMode(task.token, WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW) + wct.setWindowingMode(task.token, WINDOWING_MODE_MULTI_WINDOW) wct.setBounds(task.token, Rect()) wct.setDensityDpi(task.token, getDefaultDensityDpi()) if (Transitions.ENABLE_SHELL_TRANSITIONS) { @@ -328,6 +353,13 @@ class DesktopTasksController( } } + private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) { + if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) { + splitScreenController.prepareExitSplitScreen(wct, + splitScreenController.getStageOfTask(taskInfo.taskId), EXIT_REASON_ENTER_DESKTOP) + } + } + /** * The second part of the animated move to desktop transition, called after * {@link startMoveToDesktop}. Move a task to fullscreen after being dragged from fullscreen @@ -493,6 +525,55 @@ class DesktopTasksController( } } + /** + * Quick-resize to the right or left half of the stable bounds. + * + * @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to. + */ + fun snapToHalfScreen( + taskInfo: RunningTaskInfo, + windowDecor: DesktopModeWindowDecoration, + position: SnapPosition + ) { + val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return + + val stableBounds = Rect() + displayLayout.getStableBounds(stableBounds) + + val destinationWidth = stableBounds.width() / 2 + val destinationBounds = when (position) { + SnapPosition.LEFT -> { + Rect( + stableBounds.left, + stableBounds.top, + stableBounds.left + destinationWidth, + stableBounds.bottom + ) + } + SnapPosition.RIGHT -> { + Rect( + stableBounds.right - destinationWidth, + stableBounds.top, + stableBounds.right, + stableBounds.bottom + ) + } + } + + if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return + + val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + toggleResizeDesktopTaskTransitionHandler.startTransition( + wct, + taskInfo.taskId, + windowDecor + ) + } else { + shellTaskOrganizer.applyTransaction(wct) + } + } + private fun getDefaultDesktopTaskBounds(density: Float, stableBounds: Rect, outBounds: Rect) { val width = (DESKTOP_MODE_DEFAULT_WIDTH_DP * density + 0.5f).toInt() val height = (DESKTOP_MODE_DEFAULT_HEIGHT_DP * density + 0.5f).toInt() @@ -580,6 +661,10 @@ class DesktopTasksController( val triggerTask = request.triggerTask val shouldHandleRequest = when { + recentsAnimationRunning -> { + reason = "recents animation is running" + false + } // Only handle open or to front transitions request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> { reason = "transition type not handled (${request.type})" @@ -645,10 +730,7 @@ class DesktopTasksController( finishTransaction: SurfaceControl.Transaction ) { // Add rounded corners to freeform windows - val ta: TypedArray = context.obtainStyledAttributes( - intArrayOf(R.attr.dialogCornerRadius)) - val cornerRadius = ta.getDimensionPixelSize(0, 0).toFloat() - ta.recycle() + val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context) info.changes .filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM } .forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) } @@ -921,17 +1003,17 @@ class DesktopTasksController( } /** - * Update the corner region for a specified task + * Update the exclusion region for a specified task */ - fun onTaskCornersChanged(taskId: Int, corner: Region) { - desktopModeTaskRepository.updateTaskCorners(taskId, corner) + fun onExclusionRegionChanged(taskId: Int, exclusionRegion: Region) { + desktopModeTaskRepository.updateTaskExclusionRegions(taskId, exclusionRegion) } /** - * Remove a previously tracked corner region for a specified task. + * Remove a previously tracked exclusion region for a specified task. */ - fun removeCornersForTask(taskId: Int) { - desktopModeTaskRepository.removeTaskCorners(taskId) + fun removeExclusionRegionForTask(taskId: Int) { + desktopModeTaskRepository.removeExclusionRegion(taskId) } /** @@ -945,16 +1027,16 @@ class DesktopTasksController( } /** - * Adds a listener to track changes to desktop task corners + * Adds a listener to track changes to desktop task gesture exclusion regions * * @param listener the listener to add. * @param callbackExecutor the executor to call the listener on. */ - fun setTaskCornerListener( + fun setTaskRegionListener( listener: Consumer<Region>, callbackExecutor: Executor ) { - desktopModeTaskRepository.setTaskCornerListener(listener, callbackExecutor) + desktopModeTaskRepository.setExclusionRegionListener(listener, callbackExecutor) } private fun dump(pw: PrintWriter, prefix: String) { @@ -980,7 +1062,7 @@ class DesktopTasksController( callbackExecutor: Executor ) { mainExecutor.execute { - this@DesktopTasksController.setTaskCornerListener(listener, callbackExecutor) + this@DesktopTasksController.setTaskRegionListener(listener, callbackExecutor) } } } @@ -1037,11 +1119,11 @@ class DesktopTasksController( controller = null } - override fun showDesktopApps(displayId: Int) { + override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) { ExecutorUtils.executeRemoteCallWithTaskPermission( controller, "showDesktopApps" - ) { c -> c.showDesktopApps(displayId) } + ) { c -> c.showDesktopApps(displayId, remoteTransition) } } override fun stashDesktopApps(displayId: Int) { @@ -1117,4 +1199,7 @@ class DesktopTasksController( return DESKTOP_DENSITY_OVERRIDE in DESKTOP_DENSITY_ALLOWED_RANGE } } + + /** The positions on a screen that a task can snap to. */ + enum class SnapPosition { RIGHT, LEFT } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java index 1128d91bb5ad..024465b281b8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java @@ -271,8 +271,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition startT.apply(); - mTransitions.getMainExecutor().execute( - () -> finishCallback.onTransitionFinished(null)); + mTransitions.getMainExecutor().execute(() -> finishCallback.onTransitionFinished(null)); return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl index 47edfd455f5a..6bdaf1eadb8a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl @@ -17,6 +17,7 @@ package com.android.wm.shell.desktopmode; import android.app.ActivityManager.RunningTaskInfo; +import android.window.RemoteTransition; import com.android.wm.shell.desktopmode.IDesktopTaskListener; /** @@ -25,7 +26,7 @@ import com.android.wm.shell.desktopmode.IDesktopTaskListener; interface IDesktopMode { /** Show apps on the desktop on the given display */ - void showDesktopApps(int displayId); + void showDesktopApps(int displayId, in RemoteTransition remoteTransition); /** Stash apps on the desktop to allow launching another app from home screen */ void stashDesktopApps(int displayId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java index 22541bbd892a..a80241e0ac5c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -68,7 +68,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, private void onInit() { mShellTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_FREEFORM); - if (DesktopModeStatus.isAnyEnabled()) { + if (DesktopModeStatus.isEnabled()) { mShellTaskOrganizer.addFocusListener(this); } } @@ -90,7 +90,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, t.apply(); } - if (DesktopModeStatus.isAnyEnabled()) { + if (DesktopModeStatus.isEnabled()) { mDesktopModeTaskRepository.ifPresent(repository -> { repository.addOrMoveFreeformTaskToTop(taskInfo.taskId); if (taskInfo.isVisible) { @@ -111,7 +111,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, taskInfo.taskId); mTasks.remove(taskInfo.taskId); - if (DesktopModeStatus.isAnyEnabled()) { + if (DesktopModeStatus.isEnabled()) { mDesktopModeTaskRepository.ifPresent(repository -> { repository.removeFreeformTask(taskInfo.taskId); if (repository.removeActiveTask(taskInfo.taskId)) { @@ -135,7 +135,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, taskInfo.taskId); mWindowDecorationViewModel.onTaskInfoChanged(taskInfo); state.mTaskInfo = taskInfo; - if (DesktopModeStatus.isAnyEnabled()) { + if (DesktopModeStatus.isEnabled()) { mDesktopModeTaskRepository.ifPresent(repository -> { if (taskInfo.isVisible) { if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) { @@ -154,7 +154,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Focus Changed: #%d focused=%b", taskInfo.taskId, taskInfo.isFocused); - if (DesktopModeStatus.isAnyEnabled() && taskInfo.isFocused) { + if (DesktopModeStatus.isEnabled() && taskInfo.isFocused) { mDesktopModeTaskRepository.ifPresent(repository -> { repository.addOrMoveFreeformTaskToTop(taskInfo.taskId); }); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/performance/PerfHintController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/performance/PerfHintController.kt new file mode 100644 index 000000000000..f7977f88006e --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/performance/PerfHintController.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.performance + +import android.content.Context +import android.os.PerformanceHintManager +import android.os.Process +import android.window.SystemPerformanceHinter +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.sysui.ShellCommandHandler +import com.android.wm.shell.sysui.ShellInit +import java.io.PrintWriter +import java.util.concurrent.TimeUnit + +/** + * Manages the performance hints to the system. + */ +class PerfHintController(private val mContext: Context, + shellInit: ShellInit, + private val mShellCommandHandler: ShellCommandHandler, + rootTdaOrganizer: RootTaskDisplayAreaOrganizer) { + + // The system perf hinter + val hinter: SystemPerformanceHinter + + init { + hinter = SystemPerformanceHinter(mContext, + rootTdaOrganizer.performanceRootProvider) + shellInit.addInitCallback(this::onInit, this) + } + + private fun onInit() { + mShellCommandHandler.addDumpCallback(this::dump, this) + val perfHintMgr = mContext.getSystemService(PerformanceHintManager::class.java) + val adpfSession = perfHintMgr!!.createHintSession(intArrayOf(Process.myTid()), + TimeUnit.SECONDS.toNanos(1)) + hinter.setAdpfSession(adpfSession) + } + + fun dump(pw: PrintWriter, prefix: String?) { + hinter.dump(pw, prefix) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java index bc17ce97dbff..118ad9c4bfe3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java @@ -383,6 +383,9 @@ public class PipAccessibilityInteractionConnection { } @Override - public void attachAccessibilityOverlayToWindow(SurfaceControl sc) {} + public void attachAccessibilityOverlayToWindow( + SurfaceControl sc, + int interactionId, + IAccessibilityInteractionConnectionCallback callback) {} } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 106486714a5c..63f20fd8e997 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -797,21 +797,15 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipBoundsAlgorithm.getMovementBounds(postChangeBounds), mPipBoundsState.getStashedState()); - // Scale PiP on density dpi change, so it appears to be the same size physically. - final boolean densityDpiChanged = - mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0 - && (mPipDisplayLayoutState.getDisplayLayout().densityDpi() - != layout.densityDpi()); - if (densityDpiChanged) { - final float scale = (float) layout.densityDpi() - / mPipDisplayLayoutState.getDisplayLayout().densityDpi(); - postChangeBounds.set(0, 0, - (int) (postChangeBounds.width() * scale), - (int) (postChangeBounds.height() * scale)); - } - updateDisplayLayout.run(); + // Resize the PiP bounds to be at the same scale relative to the new size spec. For + // example, if PiP was resized to 90% of the maximum size on the previous layout, + // make sure it is 90% of the new maximum size spec. + postChangeBounds.set(0, 0, + (int) (mPipBoundsState.getMaxSize().x * mPipBoundsState.getBoundsScale()), + (int) (mPipBoundsState.getMaxSize().y * mPipBoundsState.getBoundsScale())); + // Calculate the PiP bounds in the new orientation based on same fraction along the // rotated movement bounds. final Rect postChangeMovementBounds = mPipBoundsAlgorithm.getMovementBounds( @@ -822,6 +816,15 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipDisplayLayoutState.getDisplayBounds(), mPipDisplayLayoutState.getDisplayLayout().stableInsets()); + // make sure we user resize to the updated bounds to avoid animating to any outdated + // sizes from the previous layout upon double tap CUJ + mPipBoundsState.setHasUserResizedPip(true); + mTouchHandler.setUserResizeBounds(postChangeBounds); + + final boolean densityDpiChanged = + mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0 + && (mPipDisplayLayoutState.getDisplayLayout().densityDpi() + != layout.densityDpi()); if (densityDpiChanged) { // Using PipMotionHelper#movePip directly here may cause race condition since // the app content in PiP mode may or may not be updated for the new density dpi. @@ -833,15 +836,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb // Directly move PiP to its final destination bounds without animation. mPipTaskOrganizer.scheduleFinishResizePip(postChangeBounds); } - - // if the pip window size is beyond allowed bounds user resize to normal bounds - if (mPipBoundsState.getBounds().width() < mPipBoundsState.getMinSize().x - || mPipBoundsState.getBounds().width() > mPipBoundsState.getMaxSize().x - || mPipBoundsState.getBounds().height() < mPipBoundsState.getMinSize().y - || mPipBoundsState.getBounds().height() > mPipBoundsState.getMaxSize().y) { - mTouchHandler.userResizeTo(mPipBoundsState.getNormalBounds(), snapFraction); - } - } else { updateDisplayLayout.run(); } 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 e5f9fdc7a740..f175775ce8b2 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,6 @@ */ package com.android.wm.shell.pip.phone; -import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_PINCH_RESIZE; 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; @@ -31,7 +30,6 @@ import android.graphics.Rect; import android.graphics.Region; import android.hardware.input.InputManager; import android.os.Looper; -import android.provider.DeviceConfig; import android.view.BatchedInputEventReceiver; import android.view.Choreographer; import android.view.InputChannel; @@ -155,21 +153,8 @@ public class PipResizeGestureHandler { mContext.getDisplay().getRealSize(mMaxSize); reloadResources(); - mEnablePinchResize = DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_SYSTEMUI, - PIP_PINCH_RESIZE, - /* defaultValue = */ true); - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, - mMainExecutor, - new DeviceConfig.OnPropertiesChangedListener() { - @Override - public void onPropertiesChanged(DeviceConfig.Properties properties) { - if (properties.getKeyset().contains(PIP_PINCH_RESIZE)) { - mEnablePinchResize = properties.getBoolean( - PIP_PINCH_RESIZE, /* defaultValue = */ true); - } - } - }); + final Resources res = mContext.getResources(); + mEnablePinchResize = res.getBoolean(R.bool.config_pipEnablePinchResize); } public void onConfigurationChanged() { @@ -579,6 +564,12 @@ public class PipResizeGestureHandler { resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y); } + // If user resize is smaller than min size, auto resize to min + if (mLastResizeBounds.width() < mMinSize.x + || mLastResizeBounds.height() < mMinSize.y) { + resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y); + } + // get the current movement bounds final Rect movementBounds = mPipBoundsAlgorithm .getMovementBounds(mLastResizeBounds); @@ -679,6 +670,8 @@ public class PipResizeGestureHandler { pw.println(innerPrefix + "mEnablePinchResize=" + mEnablePinchResize); pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed); pw.println(innerPrefix + "mOhmOffset=" + mOhmOffset); + pw.println(innerPrefix + "mMinSize=" + mMinSize); + pw.println(innerPrefix + "mMaxSize=" + mMaxSize); } class PipResizeInputEventReceiver extends BatchedInputEventReceiver { 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 2ce4fb9e297b..452a41696fcf 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 @@ -779,13 +779,10 @@ public class PipTouchHandler { } /** - * Resizes the pip window and updates user resized bounds - * - * @param bounds target bounds to resize to - * @param snapFraction snap fraction to apply after resizing + * Sets the user resize bounds tracked by {@link PipResizeGestureHandler} */ - void userResizeTo(Rect bounds, float snapFraction) { - mPipResizeGestureHandler.userResizeTo(bounds, snapFraction); + void setUserResizeBounds(Rect bounds) { + mPipResizeGestureHandler.setUserResizeBounds(bounds); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java index 5f6b3fe1e250..fc0b876e1bde 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java @@ -77,6 +77,19 @@ abstract class TvPipAction { return mActionType; } + static String getActionTypeString(@ActionType int actionType) { + switch (actionType) { + case ACTION_FULLSCREEN: return "ACTION_FULLSCREEN"; + case ACTION_CLOSE: return "ACTION_CLOSE"; + case ACTION_MOVE: return "ACTION_MOVE"; + case ACTION_EXPAND_COLLAPSE: return "ACTION_EXPAND_COLLAPSE"; + case ACTION_CUSTOM: return "ACTION_CUSTOM"; + case ACTION_CUSTOM_CLOSE: return "ACTION_CUSTOM_CLOSE"; + default: + return "UNDEFINED"; + } + } + abstract void populateButton(@NonNull TvWindowMenuActionButton button, Handler mainHandler); abstract PendingIntent getPendingIntent(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java index 4bba9690707a..6b890c49b713 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java @@ -56,8 +56,10 @@ public class TvPipActionsProvider implements TvPipAction.SystemActionsHandler { private final List<Listener> mListeners = new ArrayList<>(); private final TvPipAction.SystemActionsHandler mSystemActionsHandler; - private final List<TvPipAction> mActionsList; + private final List<TvPipAction> mActionsList = new ArrayList<>(); + private final TvPipSystemAction mFullscreenAction; private final TvPipSystemAction mDefaultCloseAction; + private final TvPipSystemAction mMoveAction; private final TvPipSystemAction mExpandCollapseAction; private final List<RemoteAction> mMediaActions = new ArrayList<>(); @@ -67,26 +69,27 @@ public class TvPipActionsProvider implements TvPipAction.SystemActionsHandler { TvPipAction.SystemActionsHandler systemActionsHandler) { mSystemActionsHandler = systemActionsHandler; - mActionsList = new ArrayList<>(); - mActionsList.add(new TvPipSystemAction(ACTION_FULLSCREEN, R.string.pip_fullscreen, + mFullscreenAction = new TvPipSystemAction(ACTION_FULLSCREEN, R.string.pip_fullscreen, R.drawable.pip_ic_fullscreen_white, ACTION_TO_FULLSCREEN, context, - mSystemActionsHandler)); - + mSystemActionsHandler); mDefaultCloseAction = new TvPipSystemAction(ACTION_CLOSE, R.string.pip_close, R.drawable.pip_ic_close_white, ACTION_CLOSE_PIP, context, mSystemActionsHandler); - mActionsList.add(mDefaultCloseAction); - - mActionsList.add(new TvPipSystemAction(ACTION_MOVE, R.string.pip_move, - R.drawable.pip_ic_move_white, ACTION_MOVE_PIP, context, mSystemActionsHandler)); - + mMoveAction = new TvPipSystemAction(ACTION_MOVE, R.string.pip_move, + R.drawable.pip_ic_move_white, ACTION_MOVE_PIP, context, mSystemActionsHandler); mExpandCollapseAction = new TvPipSystemAction(ACTION_EXPAND_COLLAPSE, R.string.pip_collapse, R.drawable.pip_ic_collapse, ACTION_TOGGLE_EXPANDED_PIP, context, mSystemActionsHandler); - mActionsList.add(mExpandCollapseAction); + initActions(); pipMediaController.addActionListener(this::onMediaActionsChanged); } + private void initActions() { + mActionsList.add(mFullscreenAction); + mActionsList.add(mDefaultCloseAction); + mActionsList.add(mMoveAction); + } + @Override public void executeAction(@TvPipAction.ActionType int actionType) { if (mSystemActionsHandler != null) { @@ -199,6 +202,14 @@ public class TvPipActionsProvider implements TvPipAction.SystemActionsHandler { } } + void reset() { + mActionsList.clear(); + mMediaActions.clear(); + mAppActions.clear(); + + initActions(); + } + List<TvPipAction> getActionsList() { return mActionsList; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java index 47a8cc89559e..2b3a93e3c3e8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java @@ -81,6 +81,7 @@ public class TvPipBoundsState extends PipBoundsState { super(context, sizeSpecSource, pipDisplayLayoutState); mContext = context; updateDefaultGravity(); + mTvPipGravity = mDefaultGravity; mPreviousCollapsedGravity = mDefaultGravity; mIsTvExpandedPipSupported = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE); 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 46336ce0655a..72115fdefa05 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 @@ -478,6 +478,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal mActionBroadcastReceiver.unregister(); mTvPipMenuController.closeMenu(); + mTvPipActionsProvider.reset(); mTvPipBoundsState.resetTvPipState(); mTvPipBoundsController.reset(); setState(STATE_NO_PIP); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index b2a189b45d6c..ee55211a73a9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -62,13 +62,16 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis private SurfaceControl mLeash; private TvPipMenuView mPipMenuView; private TvPipBackgroundView mPipBackgroundView; - private boolean mMenuIsFocused; @TvPipMenuMode private int mCurrentMenuMode = MODE_NO_MENU; @TvPipMenuMode private int mPrevMenuMode = MODE_NO_MENU; + /** When the window gains focus, enter this menu mode */ + @TvPipMenuMode + private int mMenuModeOnFocus = MODE_ALL_ACTIONS_MENU; + @IntDef(prefix = { "MODE_" }, value = { MODE_NO_MENU, MODE_MOVE_MENU, @@ -170,6 +173,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis mPipMenuView = createTvPipMenuView(); setUpViewSurfaceZOrder(mPipMenuView, 1); addPipMenuViewToSystemWindows(mPipMenuView, MENU_WINDOW_TITLE); + mPipMenuView.getViewTreeObserver().addOnWindowFocusChangeListener(hasFocus -> { + onPipWindowFocusChanged(hasFocus); + }); } @VisibleForTesting @@ -224,13 +230,14 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis void showMovementMenu() { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMovementMenu()", TAG); - switchToMenuMode(MODE_MOVE_MENU); + requestMenuMode(MODE_MOVE_MENU); } @Override public void showMenu() { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMenu()", TAG); - switchToMenuMode(MODE_ALL_ACTIONS_MENU, true); + mPipMenuView.resetMenu(); + requestMenuMode(MODE_ALL_ACTIONS_MENU); } void onPipTransitionToTargetBoundsStarted(Rect targetBounds) { @@ -250,7 +257,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis void closeMenu() { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: closeMenu()", TAG); - switchToMenuMode(MODE_NO_MENU); + requestMenuMode(MODE_NO_MENU); } @Override @@ -392,11 +399,15 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis } } - // Start methods handling {@link TvPipMenuMode} + // Beginning of convenience methods for {@link TvPipMenuMode} @VisibleForTesting boolean isMenuOpen() { - return mCurrentMenuMode != MODE_NO_MENU; + return isMenuOpen(mCurrentMenuMode); + } + + private static boolean isMenuOpen(@TvPipMenuMode int menuMode) { + return menuMode != MODE_NO_MENU; } @VisibleForTesting @@ -409,31 +420,93 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis return mCurrentMenuMode == MODE_ALL_ACTIONS_MENU; } - private void switchToMenuMode(@TvPipMenuMode int menuMode) { - switchToMenuMode(menuMode, false); + @VisibleForTesting + String getMenuModeString() { + return getMenuModeString(mCurrentMenuMode); } - private void switchToMenuMode(@TvPipMenuMode int menuMode, boolean resetMenu) { - // Note: we intentionally don't return early here, because the TvPipMenuView needs to - // refresh the Ui even if there is no menu mode change. - mPrevMenuMode = mCurrentMenuMode; - mCurrentMenuMode = menuMode; + static String getMenuModeString(@TvPipMenuMode int menuMode) { + switch(menuMode) { + case MODE_NO_MENU: + return "MODE_NO_MENU"; + case MODE_MOVE_MENU: + return "MODE_MOVE_MENU"; + case MODE_ALL_ACTIONS_MENU: + return "MODE_ALL_ACTIONS_MENU"; + default: + return "Unknown"; + } + } + + // Beginning of methods handling switching between menu modes + + private void requestMenuMode(@TvPipMenuMode int menuMode) { + if (isMenuOpen() == isMenuOpen(menuMode)) { + // No need to request a focus change. We can directly switch to the new mode. + switchToMenuMode(menuMode); + } else { + if (isMenuOpen(menuMode)) { + mMenuModeOnFocus = menuMode; + } + + // Send a request to gain window focus if the menu is open, or lose window focus + // otherwise. Once the focus change happens, we will request the new mode in the + // callback {@link #onPipWindowFocusChanged}. + requestPipMenuFocus(isMenuOpen(menuMode)); + } + // Note: we don't handle cases where there is a focus change currently in flight, because + // this is very unlikely to happen in practice and would complicate the logic. + } + + private void requestPipMenuFocus(boolean focus) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: requestPipMenuFocus(%b)", TAG, focus); + + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, + mSystemWindows.getFocusGrantToken(mPipMenuView), focus); + } catch (Exception e) { + ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: Unable to update focus, %s", TAG, e); + } + } + + /** + * Called when the menu window gains or loses focus. + */ + @VisibleForTesting + void onPipWindowFocusChanged(boolean focused) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: onPipWindowFocusChanged - focused=%b", TAG, focused); + switchToMenuMode(focused ? mMenuModeOnFocus : MODE_NO_MENU); + + // Reset the default menu mode for focused state. + mMenuModeOnFocus = MODE_ALL_ACTIONS_MENU; + } + + /** + * Immediately switches to the menu mode in the given request. Updates the mDelegate and the UI. + * Doesn't handle any focus changes. + */ + private void switchToMenuMode(@TvPipMenuMode int menuMode) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: switchToMenuMode: from=%s, to=%s", TAG, getMenuModeString(), + getMenuModeString(menuMode)); - ProtoLog.i(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: switchToMenuMode: setting mCurrentMenuMode=%s, mPrevMenuMode=%s", TAG, - getMenuModeString(), getMenuModeString(mPrevMenuMode)); + if (mCurrentMenuMode == menuMode) return; - updateUiOnNewMenuModeRequest(resetMenu); + mPrevMenuMode = mCurrentMenuMode; + mCurrentMenuMode = menuMode; + updateUiOnNewMenuModeRequest(); updateDelegateOnNewMenuModeRequest(); } - private void updateUiOnNewMenuModeRequest(boolean resetMenu) { + private void updateUiOnNewMenuModeRequest() { if (mPipMenuView == null || mPipBackgroundView == null) return; mPipMenuView.setPipGravity(mTvPipBoundsState.getTvPipGravity()); - mPipMenuView.transitionToMenuMode(mCurrentMenuMode, resetMenu); + mPipMenuView.transitionToMenuMode(mCurrentMenuMode); mPipBackgroundView.transitionToMenuMode(mCurrentMenuMode); - grantPipMenuFocus(mCurrentMenuMode != MODE_NO_MENU); } private void updateDelegateOnNewMenuModeRequest() { @@ -444,29 +517,11 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis mDelegate.onInMoveModeChanged(); } - if (mCurrentMenuMode == MODE_NO_MENU) { + if (!isMenuOpen()) { mDelegate.onMenuClosed(); } } - @VisibleForTesting - String getMenuModeString() { - return getMenuModeString(mCurrentMenuMode); - } - - static String getMenuModeString(@TvPipMenuMode int menuMode) { - switch(menuMode) { - case MODE_NO_MENU: - return "MODE_NO_MENU"; - case MODE_MOVE_MENU: - return "MODE_MOVE_MENU"; - case MODE_ALL_ACTIONS_MENU: - return "MODE_ALL_ACTIONS_MENU"; - default: - return "Unknown"; - } - } - // Start {@link TvPipMenuView.Delegate} methods @Override @@ -476,42 +531,19 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis } @Override - public void onBackPress() { - if (!onExitMoveMode()) { - closeMenu(); - } - } - - @Override - public boolean onExitMoveMode() { + public void onExitCurrentMenuMode() { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: onExitMoveMode - mCurrentMenuMode=%s", TAG, getMenuModeString()); - - final int saveMenuMode = mCurrentMenuMode; - if (isInMoveMode()) { - switchToMenuMode(mPrevMenuMode); - } - return saveMenuMode == MODE_MOVE_MENU; + "%s: onExitCurrentMenuMode - mCurrentMenuMode=%s", TAG, getMenuModeString()); + requestMenuMode(isInMoveMode() ? mPrevMenuMode : MODE_NO_MENU); } @Override - public boolean onPipMovement(int keycode) { + public void onPipMovement(int keycode) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: onPipMovement - mCurrentMenuMode=%s", TAG, getMenuModeString()); if (isInMoveMode()) { mDelegate.movePip(keycode); } - return isInMoveMode(); - } - - @Override - public void onPipWindowFocusChanged(boolean focused) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: onPipWindowFocusChanged - focused=%b", TAG, focused); - mMenuIsFocused = focused; - if (!focused && isMenuOpen()) { - closeMenu(); - } } interface Delegate { @@ -524,21 +556,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis void closeEduText(); } - private void grantPipMenuFocus(boolean grantFocus) { - if (mMenuIsFocused == grantFocus) return; - - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: grantWindowFocus(%b)", TAG, grantFocus); - - try { - WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, - mSystemWindows.getFocusGrantToken(mPipMenuView), grantFocus); - } catch (Exception e) { - ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: Unable to update focus, %s", TAG, e); - } - } - private class PipMenuSurfaceChangedCallback implements ViewRootImpl.SurfaceChangedCallback { private final View mView; private final int mZOrder; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java index 45e1cde8f9a9..57439a59ccca 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -328,7 +328,7 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L return menuUiBounds; } - void transitionToMenuMode(int menuMode, boolean resetMenu) { + void transitionToMenuMode(int menuMode) { switch (menuMode) { case MODE_NO_MENU: hideAllUserControls(); @@ -337,7 +337,7 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L showMoveMenu(); break; case MODE_ALL_ACTIONS_MENU: - showAllActionsMenu(resetMenu); + showAllActionsMenu(); break; default: throw new IllegalArgumentException( @@ -362,13 +362,13 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L mEduTextDrawer.closeIfNeeded(); } - private void showAllActionsMenu(boolean resetMenu) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: showAllActionsMenu(), resetMenu %b", TAG, resetMenu); + void resetMenu() { + scrollToFirstAction(); + } - if (resetMenu) { - scrollToFirstAction(); - } + private void showAllActionsMenu() { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: showAllActionsMenu()", TAG); if (mCurrentMenuMode == MODE_ALL_ACTIONS_MENU) return; @@ -431,12 +431,6 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L } } - @Override - public void onWindowFocusChanged(boolean hasWindowFocus) { - super.onWindowFocusChanged(hasWindowFocus); - mListener.onPipWindowFocusChanged(hasWindowFocus); - } - private void animateAlphaTo(float alpha, View view) { if (view.getAlpha() == alpha) { return; @@ -483,28 +477,28 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L if (event.getAction() == ACTION_UP) { if (event.getKeyCode() == KEYCODE_BACK) { - mListener.onBackPress(); + mListener.onExitCurrentMenuMode(); return true; } - if (mA11yManager.isEnabled()) { - return super.dispatchKeyEvent(event); - } - - switch (event.getKeyCode()) { - case KEYCODE_DPAD_UP: - case KEYCODE_DPAD_DOWN: - case KEYCODE_DPAD_LEFT: - case KEYCODE_DPAD_RIGHT: - return mListener.onPipMovement(event.getKeyCode()) || super.dispatchKeyEvent( - event); - case KEYCODE_ENTER: - case KEYCODE_DPAD_CENTER: - return mListener.onExitMoveMode() || super.dispatchKeyEvent(event); - default: - break; + if (mCurrentMenuMode == MODE_MOVE_MENU && !mA11yManager.isEnabled()) { + switch (event.getKeyCode()) { + case KEYCODE_DPAD_UP: + case KEYCODE_DPAD_DOWN: + case KEYCODE_DPAD_LEFT: + case KEYCODE_DPAD_RIGHT: + mListener.onPipMovement(event.getKeyCode()); + return true; + case KEYCODE_ENTER: + case KEYCODE_DPAD_CENTER: + mListener.onExitCurrentMenuMode(); + return true; + default: + // Dispatch key event as normal below + } } } + return super.dispatchKeyEvent(event); } @@ -529,7 +523,7 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L if (a11yEnabled) { mA11yDoneButton.setVisibility(VISIBLE); mA11yDoneButton.setOnClickListener(v -> { - mListener.onExitMoveMode(); + mListener.onExitCurrentMenuMode(); }); mA11yDoneButton.requestFocus(); mA11yDoneButton.requestAccessibilityFocus(); @@ -626,26 +620,15 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L interface Listener { - void onBackPress(); - - /** - * Called when a button for exiting move mode was pressed. - * - * @return true if the event was handled or false if the key event should be handled by the - * next receiver. - */ - boolean onExitMoveMode(); - /** - * @return whether pip movement was handled. + * Called when a button for exiting the current menu mode was pressed. */ - boolean onPipMovement(int keycode); + void onExitCurrentMenuMode(); /** - * Called when the TvPipMenuView loses focus. This also means that the TV PiP menu window - * has lost focus. + * Called when a button to move the PiP in a certain direction, indicated by keycode. */ - void onPipWindowFocusChanged(boolean focused); + void onPipMovement(int keycode); /** * The edu text closing impacts the size of the Picture-in-Picture window and influences diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS index ec09827fa4d1..6dabb3bf6f9a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS @@ -1,3 +1,4 @@ # WM shell sub-module pip owner hwwang@google.com mateuszc@google.com +gabiyev@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java new file mode 100644 index 000000000000..186cb615f4ec --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip2.phone; + +import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; + +import android.content.Context; +import android.content.res.Configuration; +import android.view.InsetsState; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.pip.PipDisplayLayoutState; +import com.android.wm.shell.common.pip.PipUtils; +import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.sysui.ConfigurationChangeListener; +import com.android.wm.shell.sysui.ShellController; +import com.android.wm.shell.sysui.ShellInit; + +/** + * Manages the picture-in-picture (PIP) UI and states for Phones. + */ +public class PipController implements ConfigurationChangeListener, + DisplayController.OnDisplaysChangedListener { + private static final String TAG = PipController.class.getSimpleName(); + + private Context mContext; + private ShellController mShellController; + private DisplayController mDisplayController; + private DisplayInsetsController mDisplayInsetsController; + private PipDisplayLayoutState mPipDisplayLayoutState; + + private PipController(Context context, + ShellInit shellInit, + ShellController shellController, + DisplayController displayController, + DisplayInsetsController displayInsetsController, + PipDisplayLayoutState pipDisplayLayoutState) { + mContext = context; + mShellController = shellController; + mDisplayController = displayController; + mDisplayInsetsController = displayInsetsController; + mPipDisplayLayoutState = pipDisplayLayoutState; + + if (PipUtils.isPip2ExperimentEnabled()) { + shellInit.addInitCallback(this::onInit, this); + } + } + + private void onInit() { + // Ensure that we have the display info in case we get calls to update the bounds before the + // listener calls back + mPipDisplayLayoutState.setDisplayId(mContext.getDisplayId()); + DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay()); + mPipDisplayLayoutState.setDisplayLayout(layout); + + mShellController.addConfigurationChangeListener(this); + mDisplayController.addDisplayWindowListener(this); + mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(), + new DisplayInsetsController.OnInsetsChangedListener() { + @Override + public void insetsChanged(InsetsState insetsState) { + onDisplayChanged(mDisplayController + .getDisplayLayout(mPipDisplayLayoutState.getDisplayId())); + } + }); + } + + /** + * Instantiates {@link PipController}, returns {@code null} if the feature not supported. + */ + public static PipController create(Context context, + ShellInit shellInit, + ShellController shellController, + DisplayController displayController, + DisplayInsetsController displayInsetsController, + PipDisplayLayoutState pipDisplayLayoutState) { + if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { + ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: Device doesn't support Pip feature", TAG); + return null; + } + return new PipController(context, shellInit, shellController, displayController, + displayInsetsController, pipDisplayLayoutState); + } + + + @Override + public void onConfigurationChanged(Configuration newConfiguration) { + mPipDisplayLayoutState.onConfigurationChanged(); + } + + @Override + public void onThemeChanged() { + onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay())); + } + + @Override + public void onDisplayAdded(int displayId) { + if (displayId != mPipDisplayLayoutState.getDisplayId()) { + return; + } + onDisplayChanged(mDisplayController.getDisplayLayout(displayId)); + } + + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + if (displayId != mPipDisplayLayoutState.getDisplayId()) { + return; + } + onDisplayChanged(mDisplayController.getDisplayLayout(displayId)); + } + + private void onDisplayChanged(DisplayLayout layout) { + mPipDisplayLayoutState.setDisplayLayout(layout); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index b8e4c04ac262..d704b091754f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -14,9 +14,14 @@ * limitations under the License. */ -package com.android.wm.shell.pip2; +package com.android.wm.shell.pip2.phone; + +import static android.view.WindowManager.TRANSIT_OPEN; import android.annotation.NonNull; +import android.app.ActivityManager; +import android.app.PictureInPictureParams; +import android.graphics.Rect; import android.os.IBinder; import android.view.SurfaceControl; import android.window.TransitionInfo; @@ -34,8 +39,13 @@ import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; -/** Placeholder, for demonstrate purpose only. */ +/** + * Implementation of transitions for PiP on phone. + */ public class PipTransition extends PipTransitionController { + @Nullable + private IBinder mAutoEnterButtonNavTransition; + public PipTransition( @NonNull ShellInit shellInit, @NonNull ShellTaskOrganizer shellTaskOrganizer, @@ -58,15 +68,63 @@ public class PipTransition extends PipTransitionController { @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { + if (isAutoEnterInButtonNavigation(request)) { + mAutoEnterButtonNavTransition = transition; + return getEnterPipTransaction(transition, request); + } return null; } @Override + public void augmentRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request, + @NonNull WindowContainerTransaction outWct) { + if (isAutoEnterInButtonNavigation(request)) { + outWct.merge(getEnterPipTransaction(transition, request), true /* transfer */); + mAutoEnterButtonNavTransition = transition; + } + } + + private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + final ActivityManager.RunningTaskInfo pipTask = request.getPipTask(); + PictureInPictureParams pipParams = pipTask.pictureInPictureParams; + mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo, + pipParams, mPipBoundsAlgorithm); + + // calculate the entry bounds and notify core to move task to pinned with final bounds + final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.movePipActivityToPinnedRootTask(pipTask.token, entryBounds); + return wct; + } + + private boolean isAutoEnterInButtonNavigation(@NonNull TransitionRequestInfo requestInfo) { + final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipTask(); + if (pipTask == null) { + return false; + } + if (pipTask.pictureInPictureParams == null) { + return false; + } + + // Assuming auto-enter is enabled and pipTask is non-null, the TRANSIT_OPEN request type + // implies that we are entering PiP in button navigation mode. This is guaranteed by + // TaskFragment#startPausing()` in Core which wouldn't get called in gesture nav. + return requestInfo.getType() == TRANSIT_OPEN + && pipTask.pictureInPictureParams.isAutoEnterEnabled(); + } + + @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + if (transition == mAutoEnterButtonNavTransition) { + startTransaction.apply(); + finishCallback.onTransitionFinished(null); + return true; + } return false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java index 069066e4bd49..2616b8b08bf1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java @@ -34,4 +34,10 @@ public interface RecentTasks { default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor, Consumer<List<GroupedRecentTaskInfo>> callback) { } + + /** + * Adds the listener to be notified of whether the recent task animation is running. + */ + default void addAnimationStateListener(Executor listenerExecutor, Consumer<Boolean> listener) { + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index f35eda6caef0..ccc34389557c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -203,6 +203,17 @@ public class RecentTasksController implements TaskStackListenerCallback, } } + @Nullable + public SplitBounds getSplitBoundsForTaskId(int taskId) { + if (taskId == INVALID_TASK_ID) { + return null; + } + + // We could do extra verification of requiring both taskIds of a pair and verifying that + // the same split bounds object is returned... but meh. Seems unnecessary. + return mTaskSplitBoundsMap.get(taskId); + } + @Override public Context getContext() { return mContext; @@ -329,7 +340,7 @@ public class RecentTasksController implements TaskStackListenerCallback, continue; } - if (DesktopModeStatus.isProto2Enabled() && mDesktopModeTaskRepository.isPresent() + if (DesktopModeStatus.isEnabled() && mDesktopModeTaskRepository.isPresent() && mDesktopModeTaskRepository.get().isActiveTask(taskInfo.taskId)) { // Freeform tasks will be added as a separate entry freeformTasks.add(taskInfo); @@ -417,6 +428,21 @@ public class RecentTasksController implements TaskStackListenerCallback, executor.execute(() -> callback.accept(tasks)); }); } + + @Override + public void addAnimationStateListener(Executor executor, Consumer<Boolean> listener) { + mMainExecutor.execute(() -> { + if (mTransitionHandler == null) { + return; + } + mTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() { + @Override + public void onAnimationStateChanged(boolean running) { + executor.execute(() -> listener.accept(running)); + } + }); + }); + } } 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 50ba8975802b..d277eef761e9 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 @@ -16,6 +16,7 @@ package com.android.wm.shell.recents; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.view.WindowManager.TRANSIT_CHANGE; @@ -23,6 +24,8 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; import static android.view.WindowManager.TRANSIT_SLEEP; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; + import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.ActivityManager; @@ -51,6 +54,7 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.IResultReceiver; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -69,8 +73,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { private final Transitions mTransitions; private final ShellExecutor mExecutor; + @Nullable + private final RecentTasksController mRecentTasksController; private IApplicationThread mAnimApp = null; private final ArrayList<RecentsController> mControllers = new ArrayList<>(); + private final ArrayList<RecentsTransitionStateListener> mStateListeners = new ArrayList<>(); /** * List of other handlers which might need to mix recents with other things. These are checked @@ -82,6 +89,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { @Nullable RecentTasksController recentTasksController) { mTransitions = transitions; mExecutor = transitions.getMainExecutor(); + mRecentTasksController = recentTasksController; if (!Transitions.ENABLE_SHELL_TRANSITIONS) return; if (recentTasksController == null) return; shellInit.addInitCallback(() -> { @@ -100,6 +108,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { mMixers.remove(mixer); } + /** Adds the callback for receiving the state change of transition. */ + public void addTransitionStateListener(RecentsTransitionStateListener listener) { + mStateListeners.add(listener); + } + @VisibleForTesting public IBinder startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options, IApplicationThread appThread, IRecentsAnimationRunner listener) { @@ -122,6 +135,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct, mixedHandler == null ? this : mixedHandler); + for (int i = 0; i < mStateListeners.size(); i++) { + mStateListeners.get(i).onTransitionStarted(transition); + } if (mixer != null) { mixer.setRecentsTransition(transition); } @@ -165,13 +181,14 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { return false; } final RecentsController controller = mControllers.get(controllerIdx); - Transitions.setRunningRemoteTransitionDelegate(mAnimApp); + final IApplicationThread animApp = mAnimApp; mAnimApp = null; if (!controller.start(info, startTransaction, finishTransaction, finishCallback)) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "RecentsTransitionHandler.startAnimation: failed to start animation"); return false; } + Transitions.setRunningRemoteTransitionDelegate(animApp); return true; } @@ -263,7 +280,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { mDeathHandler = () -> { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.DeathRecipient: binder died", mInstanceId); - finish(mWillFinishToHome, false /* leaveHint */); + finish(mWillFinishToHome, false /* leaveHint */, null /* finishCb */); }; try { mListener.asBinder().linkToDeath(mDeathHandler, 0 /* flags */); @@ -297,7 +314,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } } if (mFinishCB != null) { - finishInner(toHome, false /* userLeave */); + finishInner(toHome, false /* userLeave */, null /* finishCb */); } else { cleanUp(); } @@ -382,6 +399,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { mTransition = null; mPendingPauseSnapshotsForCancel = null; mControllers.remove(this); + for (int i = 0; i < mStateListeners.size(); i++) { + mStateListeners.get(i).onAnimationStateChanged(false); + } } boolean start(TransitionInfo info, SurfaceControl.Transaction t, @@ -426,6 +446,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { mLeashMap = new ArrayMap<>(); mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0; + int closingSplitTaskId = INVALID_TASK_ID; final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>(); final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>(); TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter(); @@ -452,6 +473,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { apps.add(target); if (TransitionUtil.isClosingType(change.getMode())) { mPausingTasks.add(new TaskState(change, target.leash)); + closingSplitTaskId = change.getTaskInfo().taskId; if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " adding pausing leaf home taskId=%d", taskInfo.taskId); @@ -509,13 +531,19 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } } t.apply(); + Bundle b = new Bundle(1 /*capacity*/); + b.putParcelable(KEY_EXTRA_SPLIT_BOUNDS, + mRecentTasksController.getSplitBoundsForTaskId(closingSplitTaskId)); try { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.start: calling onAnimationStart", mInstanceId); mListener.onAnimationStart(this, apps.toArray(new RemoteAnimationTarget[apps.size()]), wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]), - new Rect(0, 0, 0, 0), new Rect()); + new Rect(0, 0, 0, 0), new Rect(), b); + for (int i = 0; i < mStateListeners.size(); i++) { + mStateListeners.get(i).onAnimationStateChanged(true); + } } catch (RemoteException e) { Slog.e(TAG, "Error starting recents animation", e); cancel("onAnimationStart() failed"); @@ -643,7 +671,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // now and let it do its animation (since recents is going to be occluded). sendCancelWithSnapshots(); mExecutor.executeDelayed( - () -> finishInner(true /* toHome */, false /* userLeaveHint */), 0); + () -> finishInner(true /* toHome */, false /* userLeaveHint */, + null /* finishCb */), 0); return; } if (recentsOpening != null) { @@ -872,11 +901,12 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { @Override @SuppressLint("NewApi") - public void finish(boolean toHome, boolean sendUserLeaveHint) { - mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint)); + public void finish(boolean toHome, boolean sendUserLeaveHint, IResultReceiver finishCb) { + mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint, finishCb)); } - private void finishInner(boolean toHome, boolean sendUserLeaveHint) { + private void finishInner(boolean toHome, boolean sendUserLeaveHint, + IResultReceiver runnerFinishCb) { if (mFinishCB == null) { Slog.e(TAG, "Duplicate call to finish"); return; @@ -895,19 +925,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { if (toHome) wct.reorder(mRecentsTask, true /* toTop */); else wct.restoreTransientOrder(mRecentsTask); } - if (!toHome - // If a recents gesture starts on the 3p launcher, then the 3p launcher is the - // live tile (pausing app). If the gesture is "cancelled" we need to return to - // 3p launcher instead of "task-switching" away from it. - && (!mWillFinishToHome || mPausingSeparateHome) - && mPausingTasks != null && mState == STATE_NORMAL) { - if (mPausingSeparateHome) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - " returning to 3p home"); - } else { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - " returning to app"); - } + if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " returning to app"); // The gesture is returning to the pausing-task(s) rather than continuing with // recents, so end the transition by moving the app back to the top (and also // re-showing it's task). @@ -939,6 +958,15 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { wct.restoreTransientOrder(mRecentsTask); } } else { + if (mPausingSeparateHome) { + if (mOpeningTasks.isEmpty()) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " recents occluded 3p home"); + } else { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " switch task by recents on 3p home"); + } + } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " normal finish"); // The general case: committing to recents, going home, or switching tasks. for (int i = 0; i < mOpeningTasks.size(); ++i) { @@ -966,6 +994,16 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } cleanUp(); finishCB.onTransitionFinished(wct.isEmpty() ? null : wct); + 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); + } + } } @Override diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java index a401cb494822..e8733ebd8f03 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java @@ -14,14 +14,17 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.bubble +package com.android.wm.shell.recents; -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import android.os.IBinder; -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class SendBubbleNotificationTestCfArm(flicker: LegacyFlickerTest) : - SendBubbleNotificationTest(flicker) +/** The listener for the events from {@link RecentsTransitionHandler}. */ +public interface RecentsTransitionStateListener { + + /** Notifies whether the recents animation is running. */ + default void onAnimationStateChanged(boolean running) { + } + + /** Notifies that a recents shell transition has started. */ + default void onTransitionStarted(IBinder transition) {} +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl index 14304a3c0aac..253acc49071a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl @@ -91,42 +91,42 @@ interface ISplitScreen { * Starts tasks simultaneously in one transition. */ oneway void startTasks(int taskId1, in Bundle options1, int taskId2, in Bundle options2, - int splitPosition, float splitRatio, in RemoteTransition remoteTransition, + int splitPosition, int snapPosition, in RemoteTransition remoteTransition, in InstanceId instanceId) = 10; /** * Starts a pair of intent and task in one transition. */ oneway void startIntentAndTask(in PendingIntent pendingIntent, int userId1, in Bundle options1, - int taskId, in Bundle options2, int sidePosition, float splitRatio, + int taskId, in Bundle options2, int sidePosition, int snapPosition, in RemoteTransition remoteTransition, in InstanceId instanceId) = 16; /** * Starts a pair of shortcut and task in one transition. */ oneway void startShortcutAndTask(in ShortcutInfo shortcutInfo, in Bundle options1, int taskId, - in Bundle options2, int splitPosition, float splitRatio, + in Bundle options2, int splitPosition, int snapPosition, in RemoteTransition remoteTransition, in InstanceId instanceId) = 17; /** * Version of startTasks using legacy transition system. */ oneway void startTasksWithLegacyTransition(int taskId1, in Bundle options1, int taskId2, - in Bundle options2, int splitPosition, float splitRatio, + in Bundle options2, int splitPosition, int snapPosition, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 11; /** * Starts a pair of intent and task using legacy transition system. */ oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent, int userId1, - in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio, + in Bundle options1, int taskId, in Bundle options2, int splitPosition, int snapPosition, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 12; /** * Starts a pair of shortcut and task using legacy transition system. */ oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo, - in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio, + in Bundle options1, int taskId, in Bundle options2, int splitPosition, int snapPosition, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 15; /** @@ -135,7 +135,7 @@ interface ISplitScreen { oneway void startIntentsWithLegacyTransition(in PendingIntent pendingIntent1, int userId1, in ShortcutInfo shortcutInfo1, in Bundle options1, in PendingIntent pendingIntent2, int userId2, in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition, - float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 18; + int snapPosition, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 18; /** * Start a pair of intents in one transition. @@ -143,7 +143,7 @@ interface ISplitScreen { oneway void startIntents(in PendingIntent pendingIntent1, int userId1, in ShortcutInfo shortcutInfo1, in Bundle options1, in PendingIntent pendingIntent2, int userId2, in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition, - float splitRatio, in RemoteTransition remoteTransition, in InstanceId instanceId) = 19; + int snapPosition, in RemoteTransition remoteTransition, in InstanceId instanceId) = 19; /** * Blocking call that notifies and gets additional split-screen targets when entering diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index f90ee586e696..664d44910e72 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -43,6 +43,7 @@ import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.PendingIntent; import android.app.TaskInfo; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ShortcutInfo; @@ -85,6 +86,7 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ExternalThread; +import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.desktopmode.DesktopTasksController; @@ -130,6 +132,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9; public static final int EXIT_REASON_RECREATE_SPLIT = 10; public static final int EXIT_REASON_FULLSCREEN_SHORTCUT = 11; + public static final int EXIT_REASON_ENTER_DESKTOP = 12; @IntDef(value = { EXIT_REASON_UNKNOWN, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, @@ -143,6 +146,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, EXIT_REASON_CHILD_TASK_ENTER_PIP, EXIT_REASON_RECREATE_SPLIT, EXIT_REASON_FULLSCREEN_SHORTCUT, + EXIT_REASON_ENTER_DESKTOP }) @Retention(RetentionPolicy.SOURCE) @interface ExitReason{} @@ -598,8 +602,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, - InstanceId instanceId) { + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, + RemoteAnimationAdapter adapter, InstanceId instanceId) { if (options1 == null) options1 = new Bundle(); final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1); @@ -623,13 +627,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } mStageCoordinator.startShortcutAndTaskWithLegacyTransition(shortcutInfo, - activityOptions.toBundle(), taskId, options2, splitPosition, splitRatio, adapter, + activityOptions.toBundle(), taskId, options2, splitPosition, snapPosition, adapter, instanceId); } void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, - float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, + InstanceId instanceId) { if (options1 == null) options1 = new Bundle(); final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1); final String packageName1 = shortcutInfo.getPackage(); @@ -656,7 +661,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } } mStageCoordinator.startShortcutAndTask(shortcutInfo, activityOptions.toBundle(), taskId, - options2, splitPosition, splitRatio, remoteTransition, instanceId); + options2, splitPosition, snapPosition, remoteTransition, instanceId); } /** @@ -671,8 +676,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, - InstanceId instanceId) { + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, + RemoteAnimationAdapter adapter, InstanceId instanceId) { Intent fillInIntent = null; final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent); final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer); @@ -693,12 +698,12 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } } mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent, - options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId); + options1, taskId, options2, splitPosition, snapPosition, adapter, instanceId); } private void startIntentAndTask(PendingIntent pendingIntent, int userId1, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, float splitRatio, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { Intent fillInIntent = null; final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent); @@ -725,14 +730,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } } mStageCoordinator.startIntentAndTask(pendingIntent, fillInIntent, options1, taskId, - options2, splitPosition, splitRatio, remoteTransition, instanceId); + options2, splitPosition, snapPosition, remoteTransition, instanceId); } private void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, - @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, - RemoteAnimationAdapter adapter, InstanceId instanceId) { + @Nullable Bundle options2, @SplitPosition int splitPosition, + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, + InstanceId instanceId) { Intent fillInIntent1 = null; Intent fillInIntent2 = null; final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1); @@ -756,14 +762,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1, shortcutInfo1, options1, pendingIntent2, fillInIntent2, shortcutInfo2, options2, - splitPosition, splitRatio, adapter, instanceId); + splitPosition, snapPosition, adapter, instanceId); } private void startIntents(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, - @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, - @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + @Nullable Bundle options2, @SplitPosition int splitPosition, + @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, + InstanceId instanceId) { Intent fillInIntent1 = null; Intent fillInIntent2 = null; final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1); @@ -798,7 +805,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1, activityOptions1.toBundle(), pendingIntent2, fillInIntent2, shortcutInfo2, - activityOptions2.toBundle(), splitPosition, splitRatio, remoteTransition, + activityOptions2.toBundle(), splitPosition, snapPosition, remoteTransition, instanceId); } @@ -814,21 +821,22 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, final String packageName1 = SplitScreenUtils.getPackageName(intent); final String packageName2 = getPackageName(reverseSplitPosition(position)); final int userId2 = getUserId(reverseSplitPosition(position)); + final ComponentName component = intent.getIntent().getComponent(); + + // To prevent accumulating large number of instances in the background, reuse task + // in the background. If we don't explicitly reuse, new may be created even if the app + // isn't multi-instance because WM won't automatically remove/reuse the previous instance + final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional + .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1)) + .orElse(null); + if (taskInfo != null) { + startTask(taskInfo.taskId, position, options); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "Start task in background"); + return; + } if (samePackage(packageName1, packageName2, userId1, userId2)) { if (supportMultiInstancesSplit(packageName1)) { - // To prevent accumulating large number of instances in the background, reuse task - // in the background with priority. - final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional - .map(recentTasks -> recentTasks.findTaskInBackground( - intent.getIntent().getComponent(), userId1)) - .orElse(null); - if (taskInfo != null) { - startTask(taskInfo.taskId, position, options); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, - "Start task in background"); - return; - } - // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of // the split and there is no reusable background task. fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); @@ -1009,6 +1017,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return "CHILD_TASK_ENTER_PIP"; case EXIT_REASON_RECREATE_SPLIT: return "RECREATE_SPLIT"; + case EXIT_REASON_ENTER_DESKTOP: + return "ENTER_DESKTOP"; default: return "unknown reason, reason int = " + exitReason; } @@ -1217,78 +1227,82 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Override public void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, - float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, + InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startTasks", (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition( - taskId1, options1, taskId2, options2, splitPosition, - splitRatio, adapter, instanceId)); + taskId1, options1, taskId2, options2, splitPosition, snapPosition, + adapter, instanceId)); } @Override public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1, - Bundle options1, int taskId, Bundle options2, int splitPosition, float splitRatio, - RemoteAnimationAdapter adapter, InstanceId instanceId) { + Bundle options1, int taskId, Bundle options2, int splitPosition, + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, + InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startIntentAndTaskWithLegacyTransition", (controller) -> controller.startIntentAndTaskWithLegacyTransition(pendingIntent, - userId1, options1, taskId, options2, splitPosition, splitRatio, - adapter, instanceId)); + userId1, options1, taskId, options2, splitPosition, + snapPosition, adapter, instanceId)); } @Override public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, - InstanceId instanceId) { + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, + RemoteAnimationAdapter adapter, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startShortcutAndTaskWithLegacyTransition", (controller) -> controller.startShortcutAndTaskWithLegacyTransition( shortcutInfo, options1, taskId, options2, splitPosition, - splitRatio, adapter, instanceId)); + snapPosition, adapter, instanceId)); } @Override public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, - @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, + @Nullable Bundle options2, @SplitPosition int splitPosition, + @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startTasks", (controller) -> controller.mStageCoordinator.startTasks(taskId1, options1, - taskId2, options2, splitPosition, splitRatio, remoteTransition, + taskId2, options2, splitPosition, snapPosition, remoteTransition, instanceId)); } @Override public void startIntentAndTask(PendingIntent pendingIntent, int userId1, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, float splitRatio, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startIntentAndTask", (controller) -> controller.startIntentAndTask(pendingIntent, userId1, options1, - taskId, options2, splitPosition, splitRatio, remoteTransition, + taskId, options2, splitPosition, snapPosition, remoteTransition, instanceId)); } @Override public void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, - float splitRatio, @Nullable RemoteTransition remoteTransition, - InstanceId instanceId) { + @PersistentSnapPosition int snapPosition, + @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startShortcutAndTask", (controller) -> controller.startShortcutAndTask(shortcutInfo, options1, taskId, - options2, splitPosition, splitRatio, remoteTransition, instanceId)); + options2, splitPosition, snapPosition, remoteTransition, instanceId)); } @Override public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, - @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, - RemoteAnimationAdapter adapter, InstanceId instanceId) { + @Nullable Bundle options2, @SplitPosition int splitPosition, + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, + InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startIntentsWithLegacyTransition", (controller) -> controller.startIntentsWithLegacyTransition(pendingIntent1, userId1, shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2, - options2, splitPosition, splitRatio, adapter, instanceId) + options2, splitPosition, snapPosition, adapter, instanceId) ); } @@ -1296,13 +1310,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, public void startIntents(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, - @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, + @Nullable Bundle options2, @SplitPosition int splitPosition, + @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startIntents", (controller) -> controller.startIntents(pendingIntent1, userId1, shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2, options2, - splitPosition, splitRatio, remoteTransition, instanceId) + splitPosition, snapPosition, remoteTransition, instanceId) ); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java index 5483fa5d29f6..f4ab2266179a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java @@ -25,6 +25,7 @@ import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED_ import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED; import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER; import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT; +import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP; import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT; import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME; import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED; @@ -42,6 +43,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; +import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RECREATE_SPLIT; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME; @@ -192,6 +194,8 @@ public class SplitscreenEventLogger { return SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT; case EXIT_REASON_FULLSCREEN_SHORTCUT: return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT; + case EXIT_REASON_ENTER_DESKTOP: + return SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP; case EXIT_REASON_UNKNOWN: // Fall through default: diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 1c792395d22d..7a4834cb5adb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -129,6 +129,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitLayout; +import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.common.split.SplitWindowManager; @@ -143,6 +144,8 @@ import com.android.wm.shell.util.SplitBounds; import com.android.wm.shell.util.TransitionUtil; import com.android.wm.shell.windowdecor.WindowDecorViewModel; +import dalvik.annotation.optimization.NeverCompile; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; @@ -639,8 +642,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } /** Starts 2 tasks in one transition. */ - void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, - @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, + void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId2 == INVALID_TASK_ID) { @@ -662,13 +665,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, addActivityOptions(options1, mSideStage); wct.startTask(taskId1, options1); - startWithTask(wct, taskId2, options2, splitRatio, remoteTransition, instanceId); + startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId); } /** Start an intent and a task to a split pair in one transition. */ void startIntentAndTask(PendingIntent pendingIntent, Intent fillInIntent, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, float splitRatio, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId == INVALID_TASK_ID) { @@ -684,13 +687,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, addActivityOptions(options1, mSideStage); wct.sendPendingIntent(pendingIntent, fillInIntent, options1); - startWithTask(wct, taskId, options2, splitRatio, remoteTransition, instanceId); + startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId); } /** Starts a shortcut and a task to a split pair in one transition. */ void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, - float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, + InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId == INVALID_TASK_ID) { options1 = options1 != null ? options1 : new Bundle(); @@ -705,7 +709,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, addActivityOptions(options1, mSideStage); wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1); - startWithTask(wct, taskId, options2, splitRatio, remoteTransition, instanceId); + startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId); } /** @@ -716,14 +720,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)} */ private void startWithTask(WindowContainerTransaction wct, int mainTaskId, - @Nullable Bundle mainOptions, float splitRatio, + @Nullable Bundle mainOptions, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { if (!mMainStage.isActive()) { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. mMainStage.activate(wct, false /* reparent */); } - mSplitLayout.setDivideRatio(splitRatio); + mSplitLayout.setDivideRatio(snapPosition); updateWindowBounds(mSplitLayout, wct); wct.reorder(mRootTaskInfo.token, true); wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, @@ -750,7 +754,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, Intent fillInIntent2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, - @SplitPosition int splitPosition, float splitRatio, + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (pendingIntent2 == null) { @@ -772,7 +776,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } setSideStagePosition(splitPosition, wct); - mSplitLayout.setDivideRatio(splitRatio); + mSplitLayout.setDivideRatio(snapPosition); updateWindowBounds(mSplitLayout, wct); wct.reorder(mRootTaskInfo.token, true); wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, @@ -802,7 +806,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Starts a pair of tasks using legacy transition. */ void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, - float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, + InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (options1 == null) options1 = new Bundle(); if (taskId2 == INVALID_TASK_ID) { @@ -823,7 +828,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, addActivityOptions(options1, mSideStage); wct.startTask(taskId1, options1); mSplitRequest = new SplitRequest(taskId1, taskId2, splitPosition); - startWithLegacyTransition(wct, taskId2, options2, splitPosition, splitRatio, adapter, + startWithLegacyTransition(wct, taskId2, options2, splitPosition, snapPosition, adapter, instanceId); } @@ -832,8 +837,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, @Nullable PendingIntent pendingIntent2, Intent fillInIntent2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, - @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, - InstanceId instanceId) { + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, + RemoteAnimationAdapter adapter, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (options1 == null) options1 = new Bundle(); if (pendingIntent2 == null) { @@ -852,13 +857,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, pendingIntent2 != null ? pendingIntent2.getIntent() : null, splitPosition); } startWithLegacyTransition(wct, pendingIntent2, fillInIntent2, shortcutInfo2, options2, - splitPosition, splitRatio, adapter, instanceId); + splitPosition, snapPosition, adapter, instanceId); } void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, - InstanceId instanceId) { + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, + RemoteAnimationAdapter adapter, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (options1 == null) options1 = new Bundle(); if (taskId == INVALID_TASK_ID) { @@ -871,15 +876,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, addActivityOptions(options1, mSideStage); wct.sendPendingIntent(pendingIntent, fillInIntent, options1); mSplitRequest = new SplitRequest(taskId, pendingIntent.getIntent(), splitPosition); - startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter, + startWithLegacyTransition(wct, taskId, options2, splitPosition, snapPosition, adapter, instanceId); } /** Starts a pair of shortcut and task using legacy transition. */ void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, - @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, - InstanceId instanceId) { + @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, + RemoteAnimationAdapter adapter, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (options1 == null) options1 = new Bundle(); if (taskId == INVALID_TASK_ID) { @@ -890,7 +895,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, addActivityOptions(options1, mSideStage); wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1); - startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter, + startWithLegacyTransition(wct, taskId, options2, splitPosition, snapPosition, adapter, instanceId); } @@ -940,18 +945,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void startWithLegacyTransition(WindowContainerTransaction wct, @Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent, @Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle mainOptions, - @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter, - InstanceId instanceId) { + @SplitPosition int sidePosition, @PersistentSnapPosition int snapPosition, + RemoteAnimationAdapter adapter, InstanceId instanceId) { startWithLegacyTransition(wct, INVALID_TASK_ID, mainPendingIntent, mainFillInIntent, - mainShortcutInfo, mainOptions, sidePosition, splitRatio, adapter, instanceId); + mainShortcutInfo, mainOptions, sidePosition, snapPosition, adapter, instanceId); } private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId, - @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio, - RemoteAnimationAdapter adapter, InstanceId instanceId) { + @Nullable Bundle mainOptions, @SplitPosition int sidePosition, + @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, + InstanceId instanceId) { startWithLegacyTransition(wct, mainTaskId, null /* mainPendingIntent */, null /* mainFillInIntent */, null /* mainShortcutInfo */, mainOptions, sidePosition, - splitRatio, adapter, instanceId); + snapPosition, adapter, instanceId); } /** @@ -962,15 +968,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId, @Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent, @Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle options, - @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter, - InstanceId instanceId) { + @SplitPosition int sidePosition, @PersistentSnapPosition int snapPosition, + RemoteAnimationAdapter adapter, InstanceId instanceId) { if (!isSplitScreenVisible()) { exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT); } // Init divider first to make divider leash for remote animation target. mSplitLayout.init(); - mSplitLayout.setDivideRatio(splitRatio); + mSplitLayout.setDivideRatio(snapPosition); // Apply surface bounds before animation start. SurfaceControl.Transaction startT = mTransactionPool.acquire(); @@ -1778,7 +1784,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, rightBottomTaskId = sideStageTopTaskId; } SplitBounds splitBounds = new SplitBounds(topLeftBounds, bottomRightBounds, - leftTopTaskId, rightBottomTaskId); + leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition()); if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) { // Update the pair for the top tasks recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds); @@ -2733,7 +2739,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @NonNull Transitions.TransitionFinishCallback finishCallback) { boolean shouldAnimate = true; if (mSplitTransitions.isPendingEnter(transition)) { - shouldAnimate = startPendingEnterAnimation( + shouldAnimate = startPendingEnterAnimation(transition, mSplitTransitions.mPendingEnter, info, startTransaction, finishTransaction); } else if (mSplitTransitions.isPendingDismiss(transition)) { final SplitScreenTransitions.DismissSession dismiss = mSplitTransitions.mPendingDismiss; @@ -2772,7 +2778,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - private boolean startPendingEnterAnimation( + private boolean startPendingEnterAnimation(@NonNull IBinder transition, @NonNull SplitScreenTransitions.EnterSession enterTransition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { @@ -2801,21 +2807,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - if (mSplitTransitions.mPendingEnter.mExtraTransitType + SplitScreenTransitions.EnterSession pendingEnter = mSplitTransitions.mPendingEnter; + if (pendingEnter.mExtraTransitType == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) { // Open to side should only be used when split already active and foregorund. if (mainChild == null && sideChild == null) { Log.w(TAG, splitFailureMessage("startPendingEnterAnimation", "Launched a task in split, but didn't receive any task in transition.")); // This should happen when the target app is already on front, so just cancel. - mSplitTransitions.mPendingEnter.cancel(null); + pendingEnter.cancel(null); return true; } } else { if (mainChild == null || sideChild == null) { final int dismissTop = mainChild != null ? STAGE_TYPE_MAIN : (sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED); - mSplitTransitions.mPendingEnter.cancel( + pendingEnter.cancel( (cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct)); Log.w(TAG, splitFailureMessage("startPendingEnterAnimation", "launched 2 tasks in split, but didn't receive " @@ -2826,6 +2833,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mRecentTasks.isPresent() && sideChild != null) { mRecentTasks.get().removeSplitPair(sideChild.getTaskInfo().taskId); } + if (pendingEnter.mRemoteHandler != null) { + // Pass false for aborted since WM didn't abort, business logic chose to + // terminate/exit early + pendingEnter.mRemoteHandler.onTransitionConsumed(transition, + false /*aborted*/, finishT); + } mSplitUnsupportedToast.show(); return true; } @@ -3155,6 +3168,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER); } + @NeverCompile @Override public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java index 20da8773f387..edb5aba1e46b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java @@ -46,6 +46,8 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { private final int mIconFadeOutDuration; private final int mAppRevealDelay; private final int mAppRevealDuration; + @SplashScreenExitAnimationUtils.ExitAnimationType + private final int mAnimationType; private final int mAnimationDuration; private final float mIconStartAlpha; private final float mBrandingStartAlpha; @@ -91,6 +93,8 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { } mAppRevealDuration = context.getResources().getInteger( R.integer.starting_window_app_reveal_anim_duration); + mAnimationType = context.getResources().getInteger( + R.integer.starting_window_exit_animation_type); mAnimationDuration = Math.max(mIconFadeOutDuration, mAppRevealDelay + mAppRevealDuration); mMainWindowShiftLength = mainWindowShiftLength; mFinishCallback = handleFinish; @@ -98,10 +102,10 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { } void startAnimations() { - SplashScreenExitAnimationUtils.startAnimations(mSplashScreenView, mFirstWindowSurface, - mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame, mAnimationDuration, - mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha, mAppRevealDelay, - mAppRevealDuration, this, mRoundedCornerRadius); + SplashScreenExitAnimationUtils.startAnimations(mAnimationType, mSplashScreenView, + mFirstWindowSurface, mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame, + mAnimationDuration, mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha, + mAppRevealDelay, mAppRevealDuration, this, mRoundedCornerRadius); } private void reset() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java index a7e4385b60c8..e86b62dee86d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java @@ -20,6 +20,7 @@ import static android.view.Choreographer.CALLBACK_COMMIT; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.annotation.IntDef; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Configuration; @@ -54,6 +55,7 @@ import com.android.wm.shell.common.TransactionPool; public class SplashScreenExitAnimationUtils { private static final boolean DEBUG_EXIT_ANIMATION = false; private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false; + private static final boolean DEBUG_EXIT_FADE_ANIMATION = false; private static final String TAG = "SplashScreenExitAnimationUtils"; private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f); @@ -62,20 +64,47 @@ public class SplashScreenExitAnimationUtils { private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f); /** + * This splash screen exit animation type uses a radial vanish to hide + * the starting window and slides up the main window content. + * @hide + */ + public static final int TYPE_RADIAL_VANISH_SLIDE_UP = 0; + + /** + * This splash screen exit animation type fades out the starting window + * to reveal the main window content. + * @hide + */ + public static final int TYPE_FADE_OUT = 1; + + /** @hide */ + @IntDef(prefix = { "TYPE_" }, value = { + TYPE_RADIAL_VANISH_SLIDE_UP, + TYPE_FADE_OUT, + }) + public @interface ExitAnimationType {} + + /** * Creates and starts the animator to fade out the icon, reveal the app, and shift up main * window with rounded corner radius. */ - static void startAnimations(ViewGroup splashScreenView, - SurfaceControl firstWindowSurface, int mainWindowShiftLength, - TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration, - int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha, - int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener, - float roundedCornerRadius) { - ValueAnimator animator = - createAnimator(splashScreenView, firstWindowSurface, mainWindowShiftLength, - transactionPool, firstWindowFrame, animationDuration, iconFadeOutDuration, - iconStartAlpha, brandingStartAlpha, appRevealDelay, appRevealDuration, - animatorListener, roundedCornerRadius); + static void startAnimations(@ExitAnimationType int animationType, + ViewGroup splashScreenView, SurfaceControl firstWindowSurface, + int mainWindowShiftLength, TransactionPool transactionPool, Rect firstWindowFrame, + int animationDuration, int iconFadeOutDuration, float iconStartAlpha, + float brandingStartAlpha, int appRevealDelay, int appRevealDuration, + Animator.AnimatorListener animatorListener, float roundedCornerRadius) { + ValueAnimator animator; + if (animationType == TYPE_FADE_OUT) { + animator = createFadeOutAnimation(splashScreenView, animationDuration, + iconFadeOutDuration, iconStartAlpha, brandingStartAlpha, appRevealDelay, + appRevealDuration, animatorListener); + } else { + animator = createRadialVanishSlideUpAnimator(splashScreenView, + firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame, + animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha, + appRevealDelay, appRevealDuration, animatorListener, roundedCornerRadius); + } animator.start(); } @@ -89,17 +118,18 @@ public class SplashScreenExitAnimationUtils { TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration, int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha, int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) { - startAnimations(splashScreenView, firstWindowSurface, mainWindowShiftLength, - transactionPool, firstWindowFrame, animationDuration, iconFadeOutDuration, - iconStartAlpha, brandingStartAlpha, appRevealDelay, appRevealDuration, - animatorListener, 0f /* roundedCornerRadius */); + // Start the default 'reveal' animation. + startAnimations(TYPE_RADIAL_VANISH_SLIDE_UP, splashScreenView, + firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame, + animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha, + appRevealDelay, appRevealDuration, animatorListener, 0f /* roundedCornerRadius */); } /** * Creates the animator to fade out the icon, reveal the app, and shift up main window. * @hide */ - private static ValueAnimator createAnimator(ViewGroup splashScreenView, + private static ValueAnimator createRadialVanishSlideUpAnimator(ViewGroup splashScreenView, SurfaceControl firstWindowSurface, int mMainWindowShiftLength, TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration, int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha, @@ -210,6 +240,59 @@ public class SplashScreenExitAnimationUtils { return nightMode == Configuration.UI_MODE_NIGHT_YES; } + private static ValueAnimator createFadeOutAnimation(ViewGroup splashScreenView, + int animationDuration, int iconFadeOutDuration, float iconStartAlpha, + float brandingStartAlpha, int appRevealDelay, int appRevealDuration, + Animator.AnimatorListener animatorListener) { + + if (DEBUG_EXIT_FADE_ANIMATION) { + splashScreenView.setBackgroundColor(Color.BLUE); + } + + final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); + animator.setDuration(animationDuration); + animator.setInterpolator(Interpolators.LINEAR); + animator.addUpdateListener(animation -> { + + float linearProgress = (float) animation.getAnimatedValue(); + + // Icon fade out progress (always starts immediately) + final float iconFadeProgress = ICON_INTERPOLATOR.getInterpolation(getProgress( + linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration)); + View iconView = null; + View brandingView = null; + + if (splashScreenView instanceof SplashScreenView) { + iconView = ((SplashScreenView) splashScreenView).getIconView(); + brandingView = ((SplashScreenView) splashScreenView).getBrandingView(); + } + if (iconView != null) { + iconView.setAlpha(iconStartAlpha * (1f - iconFadeProgress)); + } + if (brandingView != null) { + brandingView.setAlpha(brandingStartAlpha * (1f - iconFadeProgress)); + } + + // Splash screen fade out progress (possibly delayed) + final float splashFadeProgress = Interpolators.ALPHA_OUT.getInterpolation( + getProgress(linearProgress, appRevealDelay, + appRevealDuration, animationDuration)); + + splashScreenView.setAlpha(1f - splashFadeProgress); + + if (DEBUG_EXIT_FADE_ANIMATION) { + Slog.d(TAG, "progress -> animation: " + linearProgress + + "\t icon alpha: " + ((iconView != null) ? iconView.getAlpha() : "n/a") + + "\t splash alpha: " + splashScreenView.getAlpha() + ); + } + }); + if (animatorListener != null) { + animator.addListener(animatorListener); + } + return animator; + } + /** * View which creates a circular reveal of the underlying view. * @hide diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index 29be34347b22..0c6adc942385 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -112,28 +112,15 @@ public class SplashscreenContentDrawer { */ static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION; - // The acceptable area ratio of foreground_icon_area/background_icon_area, if there is an - // icon which it's non-transparent foreground area is similar to it's background area, then - // do not enlarge the foreground drawable. - // For example, an icon with the foreground 108*108 opaque pixels and it's background - // also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon. - private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f); - - /** - * If the developer doesn't specify a background for the icon, we slightly scale it up. - * - * The background is either manually specified in the theme or the Adaptive Icon - * background is used if it's different from the window background. - */ - private static final float NO_BACKGROUND_SCALE = 192f / 160; private final Context mContext; private final HighResIconProvider mHighResIconProvider; - private int mIconSize; private int mDefaultIconSize; private int mBrandingImageWidth; private int mBrandingImageHeight; private int mMainWindowShiftLength; + private float mEnlargeForegroundIconThreshold; + private float mNoBackgroundScale; private int mLastPackageContextConfigHash; private final TransactionPool mTransactionPool; private final SplashScreenWindowAttrs mTmpAttrs = new SplashScreenWindowAttrs(); @@ -336,6 +323,10 @@ public class SplashscreenContentDrawer { com.android.wm.shell.R.dimen.starting_surface_brand_image_height); mMainWindowShiftLength = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length); + mEnlargeForegroundIconThreshold = mContext.getResources().getFloat( + com.android.wm.shell.R.dimen.splash_icon_enlarge_foreground_threshold); + mNoBackgroundScale = mContext.getResources().getFloat( + com.android.wm.shell.R.dimen.splash_icon_no_background_scale_factor); } /** @@ -604,14 +595,14 @@ public class SplashscreenContentDrawer { // There is no background below the icon, so scale the icon up if (mTmpAttrs.mIconBgColor == Color.TRANSPARENT || mTmpAttrs.mIconBgColor == mThemeColor) { - mFinalIconSize *= NO_BACKGROUND_SCALE; + mFinalIconSize *= mNoBackgroundScale; } createIconDrawable(iconDrawable, false /* legacy */, false /* loadInDetail */); } else { final float iconScale = (float) mIconSize / (float) mDefaultIconSize; final int densityDpi = mContext.getResources().getConfiguration().densityDpi; final int scaledIconDpi = - (int) (0.5f + iconScale * densityDpi * NO_BACKGROUND_SCALE); + (int) (0.5f + iconScale * densityDpi * mNoBackgroundScale); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "getIcon"); iconDrawable = mHighResIconProvider.getIcon( mActivityInfo, densityDpi, scaledIconDpi); @@ -693,8 +684,8 @@ public class SplashscreenContentDrawer { // Reference AdaptiveIcon description, outer is 108 and inner is 72, so we // scale by 192/160 if we only draw adaptiveIcon's foreground. final float noBgScale = - iconColor.mFgNonTranslucentRatio < ENLARGE_FOREGROUND_ICON_THRESHOLD - ? NO_BACKGROUND_SCALE : 1f; + iconColor.mFgNonTranslucentRatio < mEnlargeForegroundIconThreshold + ? mNoBackgroundScale : 1f; // Using AdaptiveIconDrawable here can help keep the shape consistent with the // current settings. mFinalIconSize = (int) (0.5f + mIconSize * noBgScale); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java index 0d77a2e4610c..ef8393c3b5b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java @@ -29,12 +29,16 @@ import android.content.pm.ShortcutInfo; import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; +import android.os.Handler; +import android.os.Looper; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewTreeObserver; +import com.android.internal.annotations.VisibleForTesting; + import java.util.concurrent.Executor; /** @@ -74,6 +78,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private final TaskViewTaskController mTaskViewTaskController; private Region mObscuredTouchRegion; private Insets mCaptionInsets; + private Handler mHandler; public TaskView(Context context, TaskViewTaskController taskViewTaskController) { super(context, null, 0, 0, true /* disableBackgroundLayer */); @@ -81,6 +86,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, // TODO(b/266736992): Think about a better way to set the TaskViewBase on the // TaskViewTaskController and vice-versa mTaskViewTaskController.setTaskViewBase(this); + mHandler = Handler.getMain(); getHolder().addCallback(this); } @@ -117,14 +123,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { onLocationChanged(); if (taskInfo.taskDescription != null) { - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + final int bgColor = taskInfo.taskDescription.getBackgroundColor(); + runOnViewThread(() -> setResizeBackgroundColor(bgColor)); } } @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { if (taskInfo.taskDescription != null) { - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + final int bgColor = taskInfo.taskDescription.getBackgroundColor(); + runOnViewThread(() -> setResizeBackgroundColor(bgColor)); } } @@ -143,7 +151,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, @Override public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) { - setResizeBackgroundColor(t, bgColor); + runOnViewThread(() -> setResizeBackgroundColor(t, bgColor)); } /** @@ -272,12 +280,14 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnComputeInternalInsetsListener(this); + mHandler = getHandler(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeOnComputeInternalInsetsListener(this); + mHandler = Handler.getMain(); } /** Returns the task info for the task in the TaskView. */ @@ -285,4 +295,24 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, public ActivityManager.RunningTaskInfo getTaskInfo() { return mTaskViewTaskController.getTaskInfo(); } + + /** + * Sets the handler, only for testing. + */ + @VisibleForTesting + void setHandler(Handler viewHandler) { + mHandler = viewHandler; + } + + /** + * Ensures that the given runnable runs on the view's thread. + */ + private void runOnViewThread(Runnable r) { + if (mHandler.getLooper().isCurrentThread()) { + r.run(); + } else { + // If this call is not from the same thread as the view, then post it + mHandler.post(r); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java index cefbb17d783d..e03f82526bdb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java @@ -165,19 +165,6 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { return null; } - /** - * Returns all the pending transitions for a given `taskView`. - * @param taskView the pending transition should be for this. - */ - ArrayList<PendingTransition> findAllPending(TaskViewTaskController taskView) { - ArrayList<PendingTransition> list = new ArrayList<>(); - for (int i = mPending.size() - 1; i >= 0; --i) { - if (mPending.get(i).mTaskView != taskView) continue; - list.add(mPending.get(i)); - } - return list; - } - private PendingTransition findPending(IBinder claimed) { for (int i = 0; i < mPending.size(); ++i) { if (mPending.get(i).mClaimed != claimed) continue; @@ -273,10 +260,9 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { // Task view isn't visible, the bounds will next visibility update. return; } - PendingTransition pendingOpen = findPendingOpeningTransition(taskView); - if (pendingOpen != null) { - // There is already an opening transition in-flight, the window bounds will be - // set in prepareOpenAnimation (via the window crop) if needed. + if (hasPending()) { + // There is already a transition in-flight, the window bounds will be set in + // prepareOpenAnimation. return; } WindowContainerTransaction wct = new WindowContainerTransaction(); 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 f0bb665f8082..918a5a4bd53e 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 @@ -19,8 +19,11 @@ package com.android.wm.shell.transition; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_CHANGE; +import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; @@ -42,8 +45,8 @@ import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.activityembedding.ActivityEmbeddingController; import com.android.wm.shell.common.split.SplitScreenUtils; -import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; @@ -72,9 +75,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, private RecentsTransitionHandler mRecentsHandler; private StageCoordinator mSplitHandler; private final KeyguardTransitionHandler mKeyguardHandler; - private DesktopModeController mDesktopModeController; private DesktopTasksController mDesktopTasksController; private UnfoldTransitionHandler mUnfoldHandler; + private ActivityEmbeddingController mActivityEmbeddingController; private static class MixedTransition { static final int TYPE_ENTER_PIP_FROM_SPLIT = 1; @@ -94,9 +97,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, /** Recents Transition while in desktop mode. */ static final int TYPE_RECENTS_DURING_DESKTOP = 6; - /** Fuld/Unfold transition. */ + /** Fold/Unfold transition. */ static final int TYPE_UNFOLD = 7; + /** Enter pip from one of the Activity Embedding windows. */ + static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 8; + /** The default animation for this mixed transition. */ static final int ANIM_TYPE_DEFAULT = 0; @@ -150,9 +156,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @Nullable PipTransitionController pipTransitionController, Optional<RecentsTransitionHandler> recentsHandlerOptional, KeyguardTransitionHandler keyguardHandler, - Optional<DesktopModeController> desktopModeControllerOptional, Optional<DesktopTasksController> desktopTasksControllerOptional, - Optional<UnfoldTransitionHandler> unfoldHandler) { + Optional<UnfoldTransitionHandler> unfoldHandler, + Optional<ActivityEmbeddingController> activityEmbeddingController) { mPlayer = player; mKeyguardHandler = keyguardHandler; if (Transitions.ENABLE_SHELL_TRANSITIONS @@ -170,9 +176,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, if (mRecentsHandler != null) { mRecentsHandler.addMixer(this); } - mDesktopModeController = desktopModeControllerOptional.orElse(null); mDesktopTasksController = desktopTasksControllerOptional.orElse(null); mUnfoldHandler = unfoldHandler.orElse(null); + mActivityEmbeddingController = activityEmbeddingController.orElse(null); }, this); } } @@ -195,6 +201,16 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mPipHandler.augmentRequest(transition, request, out); mSplitHandler.addEnterOrExitIfNeeded(request, out); return out; + } else if (request.getType() == TRANSIT_PIP + && (request.getFlags() & FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY) != 0 && ( + mActivityEmbeddingController != null)) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + " Got a PiP-enter request from an Activity Embedding split"); + mActiveTransitions.add(new MixedTransition( + MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING, transition)); + // Postpone transition splitting to later. + WindowContainerTransaction out = new WindowContainerTransaction(); + return out; } else if (request.getRemoteTransition() != null && TransitionUtil.isOpeningType(request.getType()) && (request.getTriggerTask() == null @@ -256,9 +272,14 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @Override public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) { - if (mRecentsHandler != null && (mSplitHandler.isSplitScreenVisible() - || DesktopModeStatus.isActive(mPlayer.getContext()))) { - return this; + if (mRecentsHandler != null) { + if (mSplitHandler.isSplitScreenVisible()) { + return this; + } else if (mDesktopTasksController != null + // Check on the default display. Recents/gesture nav is only available there + && mDesktopTasksController.getVisibleTaskCount(DEFAULT_DISPLAY) > 0) { + return this; + } } return null; } @@ -272,7 +293,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition); mixed.mLeftoversHandler = mRecentsHandler; mActiveTransitions.add(mixed); - } else if (DesktopModeStatus.isActive(mPlayer.getContext())) { + } else if (DesktopModeStatus.isEnabled()) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " + "desktop mode is active, so treat it as Mixed."); final MixedTransition mixed = new MixedTransition( @@ -353,6 +374,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction, finishCallback); + } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { + return animateEnterPipFromActivityEmbedding(mixed, info, startTransaction, + finishTransaction, finishCallback); } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) { return false; } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { @@ -398,6 +422,58 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } } + private boolean animateEnterPipFromActivityEmbedding(@NonNull MixedTransition mixed, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " + + "entering PIP from an Activity Embedding window"); + // Split into two transitions (wct) + TransitionInfo.Change pipChange = null; + final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */); + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (mPipHandler.isEnteringPip(change, info.getType())) { + if (pipChange != null) { + throw new IllegalStateException("More than 1 pip-entering changes in one" + + " transition? " + info); + } + pipChange = change; + // going backwards, so remove-by-index is fine. + everythingElse.getChanges().remove(i); + } + } + + final Transitions.TransitionFinishCallback finishCB = (wct) -> { + --mixed.mInFlightSubAnimations; + mixed.joinFinishArgs(wct); + if (mixed.mInFlightSubAnimations > 0) return; + mActiveTransitions.remove(mixed); + finishCallback.onTransitionFinished(mixed.mFinishWCT); + }; + + if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) { + // Fallback to dispatching to other handlers. + return false; + } + + // PIP window should always be on the highest Z order. + if (pipChange != null) { + mixed.mInFlightSubAnimations = 2; + mPipHandler.startEnterAnimation( + pipChange, startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE), + finishTransaction, + finishCB); + } else { + mixed.mInFlightSubAnimations = 1; + } + + mActivityEmbeddingController.startAnimation(mixed.mTransition, everythingElse, + startTransaction, finishTransaction, finishCB); + return true; + } + private boolean animateOpenIntentWithRemoteAndPip(@NonNull MixedTransition mixed, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @@ -713,17 +789,21 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + Transitions.TransitionFinishCallback finishCB = wct -> { + mixed.mInFlightSubAnimations--; + if (mixed.mInFlightSubAnimations == 0) { + mActiveTransitions.remove(mixed); + finishCallback.onTransitionFinished(wct); + } + }; + + mixed.mInFlightSubAnimations++; boolean consumed = mRecentsHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCallback); + mixed.mTransition, info, startTransaction, finishTransaction, finishCB); if (!consumed) { + mixed.mInFlightSubAnimations--; return false; } - //Sync desktop mode state (proto 1) - if (mDesktopModeController != null) { - mDesktopModeController.syncSurfaceState(info, finishTransaction); - return true; - } - //Sync desktop mode state (proto 2) if (mDesktopTasksController != null) { mDesktopTasksController.syncSurfaceState(info, finishTransaction); return true; @@ -805,6 +885,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } else { mPipHandler.end(); } + } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { + mPipHandler.end(); + mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget, + finishCallback); } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { mPipHandler.end(); if (mixed.mLeftoversHandler != null) { @@ -845,6 +929,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, if (mixed == null) return; if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { mPipHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { + mPipHandler.onTransitionConsumed(transition, aborted, finishT); + mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT); } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java index fab2dd2bf3e1..b1fc16ddf19b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java @@ -63,7 +63,7 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { @NonNull Transitions.TransitionFinishCallback finishCallback) { if (mTransition != transition) return false; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote" - + " transition %s for #%d.", mRemote, info.getDebugId()); + + " transition %s for (#%d).", mRemote, info.getDebugId()); final IBinder.DeathRecipient remoteDied = () -> { Log.e(Transitions.TAG, "Remote transition died, finishing"); @@ -152,6 +152,16 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { } @Override + public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, + @Nullable SurfaceControl.Transaction finishTransaction) { + try { + mRemote.getRemoteTransition().onTransitionConsumed(transition, aborted); + } catch (RemoteException e) { + Log.e(Transitions.TAG, "Error calling onTransitionConsumed()", e); + } + } + + @Override public String toString() { return "OneShotRemoteHandler:" + mRemote.getDebugName() + ":" + mRemote.getRemoteTransition(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java index bbf67a6155d7..ca2c3b4fbff1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -86,7 +86,16 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { @Override public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, @Nullable SurfaceControl.Transaction finishT) { - mRequestedRemotes.remove(transition); + RemoteTransition remoteTransition = mRequestedRemotes.remove(transition); + if (remoteTransition == null) { + return; + } + + try { + remoteTransition.getRemoteTransition().onTransitionConsumed(transition, aborted); + } catch (RemoteException e) { + Log.e(TAG, "Error delegating onTransitionConsumed()", e); + } } @Override @@ -117,7 +126,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { } } } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for #%d to %s", + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for (#%d) to %s", info.getDebugId(), pendingRemote); if (pendingRemote == null) return false; @@ -137,7 +146,6 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { }); } }; - Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread()); try { // If the remote is actually in the same process, then make a copy of parameters since // remote impls assume that they have to clean-up native references. @@ -149,6 +157,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { remote.getRemoteTransition().startAnimation(transition, remoteInfo, remoteStartT, cb); // assume that remote will apply the start transaction. startTransaction.clear(); + Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread()); } catch (RemoteException e) { Log.e(Transitions.TAG, "Error running remote transition.", e); unhandleDeath(remote.asBinder(), finishCallback); @@ -232,7 +241,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { if (remote == null) return null; mRequestedRemotes.put(transition, remote); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested" - + " for %s: %s", transition, remote); + + " for (#%d) %s: %s", request.getDebugId(), transition, remote); return new WindowContainerTransaction(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java index d978eafa97f3..d07d2b7b6db9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java @@ -346,7 +346,7 @@ public class TransitionAnimationHelper { .setFrameScale(1) .setPixelFormat(PixelFormat.RGBA_8888) .setChildrenOnly(true) - .setAllowProtected(true) + .setAllowProtected(false) .setCaptureSecureLayers(true) .build(); final ScreenCapture.ScreenshotHardwareBuffer edgeBuffer = 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 c74b3f30e52d..576bba96044c 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 @@ -179,6 +179,7 @@ public class Transitions implements RemoteCallable<Transitions>, private final DefaultTransitionHandler mDefaultTransitionHandler; private final RemoteTransitionHandler mRemoteTransitionHandler; private final DisplayController mDisplayController; + private final ShellCommandHandler mShellCommandHandler; private final ShellController mShellController; private final ShellTransitionImpl mImpl = new ShellTransitionImpl(); private final SleepHandler mSleepHandler = new SleepHandler(); @@ -188,9 +189,6 @@ public class Transitions implements RemoteCallable<Transitions>, /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>(); - @Nullable - private final ShellCommandHandler mShellCommandHandler; - private final ArrayList<TransitionObserver> mObservers = new ArrayList<>(); /** List of {@link Runnable} instances to run when the last active transition has finished. */ @@ -237,7 +235,7 @@ public class Transitions implements RemoteCallable<Transitions>, @Override public String toString() { if (mInfo != null && mInfo.getDebugId() >= 0) { - return "(#" + mInfo.getDebugId() + ")" + mToken + "@" + getTrack(); + return "(#" + mInfo.getDebugId() + ") " + mToken + "@" + getTrack(); } return mToken.toString() + "@" + getTrack(); } @@ -275,13 +273,14 @@ public class Transitions implements RemoteCallable<Transitions>, @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor) { - this(context, shellInit, shellController, organizer, pool, displayController, mainExecutor, - mainHandler, animExecutor, null, - new RootTaskDisplayAreaOrganizer(mainExecutor, context)); + this(context, shellInit, new ShellCommandHandler(), shellController, organizer, pool, + displayController, mainExecutor, mainHandler, animExecutor, + new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit)); } public Transitions(@NonNull Context context, @NonNull ShellInit shellInit, + @Nullable ShellCommandHandler shellCommandHandler, @NonNull ShellController shellController, @NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @@ -289,7 +288,6 @@ public class Transitions implements RemoteCallable<Transitions>, @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor, - @Nullable ShellCommandHandler shellCommandHandler, @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) { mOrganizer = organizer; mContext = context; @@ -300,13 +298,13 @@ public class Transitions implements RemoteCallable<Transitions>, mDefaultTransitionHandler = new DefaultTransitionHandler(context, shellInit, displayController, pool, mainExecutor, mainHandler, animExecutor, rootTDAOrganizer); mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor); + mShellCommandHandler = shellCommandHandler; mShellController = shellController; // The very last handler (0 in the list) should be the default one. mHandlers.add(mDefaultTransitionHandler); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default"); // Next lowest priority is remote transitions. mHandlers.add(mRemoteTransitionHandler); - mShellCommandHandler = shellCommandHandler; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote"); shellInit.addInitCallback(this::onInit, this); } @@ -339,9 +337,8 @@ public class Transitions implements RemoteCallable<Transitions>, TransitionMetrics.getInstance(); } - if (mShellCommandHandler != null) { - mShellCommandHandler.addCommandCallback("transitions", this, this); - } + mShellCommandHandler.addCommandCallback("transitions", this, this); + mShellCommandHandler.addDumpCallback(this::dump, this); } public boolean isRegistered() { @@ -655,8 +652,8 @@ public class Transitions implements RemoteCallable<Transitions>, void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady"); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s", - transitionToken, info); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s", + info.getDebugId(), transitionToken, info); final int activeIdx = findByToken(mPendingTransitions, transitionToken); if (activeIdx < 0) { throw new IllegalStateException("Got transitionReady for non-pending transition " @@ -1073,8 +1070,8 @@ public class Transitions implements RemoteCallable<Transitions>, void requestStartTransition(@NonNull IBinder transitionToken, @Nullable TransitionRequestInfo request) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s", - transitionToken, request); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s", + request.getDebugId(), transitionToken, request); if (isTransitionKnown(transitionToken)) { throw new RuntimeException("Transition already started " + transitionToken); } @@ -1475,4 +1472,68 @@ public class Transitions implements RemoteCallable<Transitions>, pw.println(prefix + "tracing"); mTracer.printShellCommandHelp(pw, prefix + " "); } + + private void dump(@NonNull PrintWriter pw, String prefix) { + pw.println(prefix + TAG); + + final String innerPrefix = prefix + " "; + pw.println(prefix + "Handlers:"); + for (TransitionHandler handler : mHandlers) { + pw.print(innerPrefix); + pw.print(handler.getClass().getSimpleName()); + pw.println(" (" + Integer.toHexString(System.identityHashCode(handler)) + ")"); + } + + pw.println(prefix + "Observers:"); + for (TransitionObserver observer : mObservers) { + pw.print(innerPrefix); + pw.println(observer.getClass().getSimpleName()); + } + + pw.println(prefix + "Pending Transitions:"); + for (ActiveTransition transition : mPendingTransitions) { + pw.print(innerPrefix + "token="); + pw.println(transition.mToken); + pw.print(innerPrefix + "id="); + pw.println(transition.mInfo != null + ? transition.mInfo.getDebugId() + : -1); + pw.print(innerPrefix + "handler="); + pw.println(transition.mHandler != null + ? transition.mHandler.getClass().getSimpleName() + : null); + } + if (mPendingTransitions.isEmpty()) { + pw.println(innerPrefix + "none"); + } + + pw.println(prefix + "Ready-during-sync Transitions:"); + for (ActiveTransition transition : mReadyDuringSync) { + pw.print(innerPrefix + "token="); + pw.println(transition.mToken); + pw.print(innerPrefix + "id="); + pw.println(transition.mInfo != null + ? transition.mInfo.getDebugId() + : -1); + pw.print(innerPrefix + "handler="); + pw.println(transition.mHandler != null + ? transition.mHandler.getClass().getSimpleName() + : null); + } + if (mReadyDuringSync.isEmpty()) { + pw.println(innerPrefix + "none"); + } + + pw.println(prefix + "Tracks:"); + for (int i = 0; i < mTracks.size(); i++) { + final ActiveTransition transition = mTracks.get(i).mActiveTransition; + pw.println(innerPrefix + "Track #" + i); + pw.print(innerPrefix + "active="); + pw.println(transition); + if (transition != null) { + pw.print(innerPrefix + "hander="); + pw.println(transition.mHandler); + } + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java index f209521b1da4..3e06d2d0e797 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java @@ -19,6 +19,8 @@ import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; +import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; + import java.util.Objects; /** @@ -26,6 +28,8 @@ import java.util.Objects; * tasks/leashes/etc in Launcher */ public class SplitBounds implements Parcelable { + public static final String KEY_EXTRA_SPLIT_BOUNDS = "key_SplitBounds"; + public final Rect leftTopBounds; public final Rect rightBottomBounds; /** This rect represents the actual gap between the two apps */ @@ -35,6 +39,7 @@ public class SplitBounds implements Parcelable { public final float leftTaskPercent; public final float dividerWidthPercent; public final float dividerHeightPercent; + public final @PersistentSnapPosition int snapPosition; /** * If {@code true}, that means at the time of creation of this object, the * split-screened apps were vertically stacked. This is useful in scenarios like @@ -45,12 +50,13 @@ public class SplitBounds implements Parcelable { public final int leftTopTaskId; public final int rightBottomTaskId; - public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, - int leftTopTaskId, int rightBottomTaskId) { + public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId, + int rightBottomTaskId, @PersistentSnapPosition int snapPosition) { this.leftTopBounds = leftTopBounds; this.rightBottomBounds = rightBottomBounds; this.leftTopTaskId = leftTopTaskId; this.rightBottomTaskId = rightBottomTaskId; + this.snapPosition = snapPosition; if (rightBottomBounds.top > leftTopBounds.top) { // vertical apps, horizontal divider @@ -81,8 +87,9 @@ public class SplitBounds implements Parcelable { appsStackedVertically = parcel.readBoolean(); leftTopTaskId = parcel.readInt(); rightBottomTaskId = parcel.readInt(); - dividerWidthPercent = parcel.readInt(); - dividerHeightPercent = parcel.readInt(); + dividerWidthPercent = parcel.readFloat(); + dividerHeightPercent = parcel.readFloat(); + snapPosition = parcel.readInt(); } @Override @@ -97,6 +104,7 @@ public class SplitBounds implements Parcelable { parcel.writeInt(rightBottomTaskId); parcel.writeFloat(dividerWidthPercent); parcel.writeFloat(dividerHeightPercent); + parcel.writeInt(snapPosition); } @Override @@ -127,7 +135,8 @@ public class SplitBounds implements Parcelable { return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n" + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId + "\n" + "Divider: " + visualDividerBounds + "\n" - + "AppsVertical? " + appsStackedVertically; + + "AppsVertical? " + appsStackedVertically + "\n" + + "snapPosition: " + snapPosition; } public static final Creator<SplitBounds> CREATOR = new Creator<SplitBounds>() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index ce8191067ae9..aff35a347183 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -18,6 +18,7 @@ package com.android.wm.shell.windowdecor; import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; +import android.app.WindowConfiguration.WindowingMode; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; @@ -64,7 +65,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL Handler handler, Choreographer choreographer, SyncTransactionQueue syncQueue) { - super(context, displayController, taskOrganizer, taskInfo, taskSurface); + super(context, displayController, taskOrganizer, taskInfo, taskSurface, + taskInfo.getConfiguration()); mHandler = handler; mChoreographer = choreographer; @@ -113,7 +115,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL mRelayoutParams.reset(); mRelayoutParams.mRunningTaskInfo = taskInfo; mRelayoutParams.mLayoutResId = R.layout.caption_window_decor; - mRelayoutParams.mCaptionHeightId = getCaptionHeightId(); + mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode()); mRelayoutParams.mShadowRadiusId = shadowRadiusID; mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; @@ -226,7 +228,12 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL } @Override - int getCaptionHeightId() { + int getCaptionHeightId(@WindowingMode int windowingMode) { return R.dimen.freeform_decor_caption_height; } + + @Override + int getCaptionViewId() { + return R.id.caption; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 3a0bdd668fbc..ca91d580b4ac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.view.WindowInsets.Type.statusBars; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; @@ -50,6 +51,8 @@ import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.InputMonitor; +import android.view.InsetsSource; +import android.view.InsetsState; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; @@ -64,22 +67,29 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.desktopmode.DesktopModeController; +import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; +import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; +import com.android.wm.shell.recents.RecentsTransitionHandler; +import com.android.wm.shell.recents.RecentsTransitionStateListener; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.KeyguardChangeListener; +import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; -import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.TaskCornersListener; +import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener; +import java.io.PrintWriter; import java.util.Optional; import java.util.function.Supplier; @@ -93,6 +103,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory; private final ActivityTaskManager mActivityTaskManager; + private final ShellCommandHandler mShellCommandHandler; private final ShellTaskOrganizer mTaskOrganizer; private final ShellController mShellController; private final Context mContext; @@ -100,13 +111,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final Choreographer mMainChoreographer; private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; - private final Optional<DesktopModeController> mDesktopModeController; private final Optional<DesktopTasksController> mDesktopTasksController; + private final RecentsTransitionHandler mRecentsTransitionHandler; private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); - private final TaskCornersListener mCornersListener = new TaskCornersListenerImpl(); + private final ExclusionRegionListener mExclusionRegionListener = + new ExclusionRegionListenerImpl(); private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId = new SparseArray<>(); @@ -120,37 +132,46 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private MoveToDesktopAnimator mMoveToDesktopAnimator; private final Rect mDragToDesktopAnimationStartBounds = new Rect(); - private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener; + private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener = + new DesktopModeKeyguardChangeListener(); + private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; + private final DisplayInsetsController mDisplayInsetsController; + private boolean mInImmersiveMode; public DesktopModeWindowDecorViewModel( Context context, Handler mainHandler, Choreographer mainChoreographer, ShellInit shellInit, + ShellCommandHandler shellCommandHandler, ShellTaskOrganizer taskOrganizer, DisplayController displayController, ShellController shellController, + DisplayInsetsController displayInsetsController, SyncTransactionQueue syncQueue, Transitions transitions, - Optional<DesktopModeController> desktopModeController, - Optional<DesktopTasksController> desktopTasksController + Optional<DesktopTasksController> desktopTasksController, + RecentsTransitionHandler recentsTransitionHandler, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer ) { this( context, mainHandler, mainChoreographer, shellInit, + shellCommandHandler, taskOrganizer, displayController, shellController, + displayInsetsController, syncQueue, transitions, - desktopModeController, desktopTasksController, + recentsTransitionHandler, new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory(), SurfaceControl.Transaction::new, - new DesktopModeKeyguardChangeListener()); + rootTaskDisplayAreaOrganizer); } @VisibleForTesting @@ -159,17 +180,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { Handler mainHandler, Choreographer mainChoreographer, ShellInit shellInit, + ShellCommandHandler shellCommandHandler, ShellTaskOrganizer taskOrganizer, DisplayController displayController, ShellController shellController, + DisplayInsetsController displayInsetsController, SyncTransactionQueue syncQueue, Transitions transitions, - Optional<DesktopModeController> desktopModeController, Optional<DesktopTasksController> desktopTasksController, + RecentsTransitionHandler recentsTransitionHandler, DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory, Supplier<SurfaceControl.Transaction> transactionFactory, - DesktopModeKeyguardChangeListener desktopModeKeyguardChangeListener) { + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { mContext = context; mMainHandler = mainHandler; mMainChoreographer = mainChoreographer; @@ -177,21 +200,31 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mTaskOrganizer = taskOrganizer; mShellController = shellController; mDisplayController = displayController; + mDisplayInsetsController = displayInsetsController; mSyncQueue = syncQueue; mTransitions = transitions; - mDesktopModeController = desktopModeController; mDesktopTasksController = desktopTasksController; - + mRecentsTransitionHandler = recentsTransitionHandler; + mShellCommandHandler = shellCommandHandler; mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory; mInputMonitorFactory = inputMonitorFactory; mTransactionFactory = transactionFactory; - mDesktopModeKeyguardChangeListener = desktopModeKeyguardChangeListener; + mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; shellInit.addInitCallback(this::onInit, this); } private void onInit() { mShellController.addKeyguardChangeListener(mDesktopModeKeyguardChangeListener); + mRecentsTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() { + @Override + public void onTransitionStarted(IBinder transition) { + onRecentsTransitionStarted(transition); + } + }); + mShellCommandHandler.addDumpCallback(this::dump, this); + mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(), + new DesktopModeOnInsetsChangedListener()); } @Override @@ -207,9 +240,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { public void onTaskStageChanged(int taskId, int stage, boolean visible) { if (visible) { DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId); - if (decor != null && DesktopModeStatus.isActive(mContext) + if (decor != null && DesktopModeStatus.isEnabled() && decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { - mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false)); mDesktopTasksController.ifPresent(c -> c.moveToSplit(decor.mTaskInfo)); } } @@ -318,8 +350,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } } + private void onRecentsTransitionStarted(IBinder transition) { + // Block relayout on window decorations originating from #onTaskInfoChanges until the + // animation completes to avoid interfering with the transition animation. + for (int i = 0; i < mWindowDecorByTaskId.size(); i++) { + final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i); + decor.incrementRelayoutBlock(); + decor.addTransitionPausingRelayout(transition); + } + } + private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener - implements View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler { + implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener, + DragDetector.MotionEventHandler{ private final int mTaskId; private final WindowContainerToken mTaskToken; @@ -345,20 +388,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { public void onClick(View v) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); final int id = v.getId(); - if (id == R.id.close_window || id == R.id.close_button) { + if (id == R.id.close_window) { mTaskOperations.closeTask(mTaskToken); - if (mSplitScreenController != null - && mSplitScreenController.isSplitScreenVisible()) { - int remainingTaskPosition = mTaskId == mSplitScreenController - .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT).taskId - ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; - ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController - .getTaskInfo(remainingTaskPosition); + if (isTaskInSplitScreen(mTaskId)) { + RunningTaskInfo remainingTask = getOtherSplitTask(mTaskId); mSplitScreenController.moveTaskToFullscreen(remainingTask.taskId); } + decoration.closeMaximizeMenu(); } else if (id == R.id.back_button) { mTaskOperations.injectBackKey(); } else if (id == R.id.caption_handle || id == R.id.open_menu_button) { + decoration.closeMaximizeMenu(); if (!decoration.isHandleMenuActive()) { moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId)); decoration.createHandleMenu(); @@ -366,7 +406,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { decoration.closeHandleMenu(); } } else if (id == R.id.desktop_button) { - mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); if (mDesktopTasksController.isPresent()) { final WindowContainerTransaction wct = new WindowContainerTransaction(); // App sometimes draws before the insets from WindowDecoration#relayout have @@ -374,10 +413,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct); decoration.incrementRelayoutBlock(); mDesktopTasksController.get().moveToDesktop(decoration, mTaskId, wct); + closeOtherSplitTask(mTaskId); } decoration.closeHandleMenu(); } else if (id == R.id.fullscreen_button) { - mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false)); mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId)); decoration.closeHandleMenu(); } else if (id == R.id.split_screen_button) { @@ -392,13 +431,35 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { // TODO(b/278084491): dev option to enable display switching // remove when select is implemented mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId)); - decoration.closeHandleMenu(); } } else if (id == R.id.maximize_window) { + moveTaskToFront(decoration.mTaskInfo); + if (decoration.isMaximizeMenuActive()) { + decoration.closeMaximizeMenu(); + return; + } final RunningTaskInfo taskInfo = decoration.mTaskInfo; mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize( taskInfo, decoration)); decoration.closeHandleMenu(); + } else if (id == R.id.maximize_menu_maximize_button) { + final RunningTaskInfo taskInfo = decoration.mTaskInfo; + mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize( + taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId))); + decoration.closeHandleMenu(); + decoration.closeMaximizeMenu(); + } else if (id == R.id.maximize_menu_snap_left_button) { + final RunningTaskInfo taskInfo = decoration.mTaskInfo; + mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen( + taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId), SnapPosition.LEFT)); + decoration.closeHandleMenu(); + decoration.closeMaximizeMenu(); + } else if (id == R.id.maximize_menu_snap_right_button) { + final RunningTaskInfo taskInfo = decoration.mTaskInfo; + mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen( + taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId), SnapPosition.RIGHT)); + decoration.closeHandleMenu(); + decoration.closeMaximizeMenu(); } } @@ -413,10 +474,26 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return mDragDetector.onMotionEvent(v, e); } + @Override + public boolean onLongClick(View v) { + final int id = v.getId(); + if (id == R.id.maximize_window) { + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); + moveTaskToFront(decoration.mTaskInfo); + if (decoration.isMaximizeMenuActive()) { + decoration.closeMaximizeMenu(); + } else { + decoration.closeHandleMenu(); + decoration.createMaximizeMenu(); + } + return true; + } + return false; + } + private void moveTaskToFront(RunningTaskInfo taskInfo) { if (!taskInfo.isFocused) { mDesktopTasksController.ifPresent(c -> c.moveTaskToFront(taskInfo)); - mDesktopModeController.ifPresent(c -> c.moveTaskToFront(taskInfo)); } } @@ -427,15 +504,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Override public boolean handleMotionEvent(@Nullable View v, MotionEvent e) { final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId); - if (DesktopModeStatus.isProto2Enabled() + if (DesktopModeStatus.isEnabled() && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { return false; } - if (DesktopModeStatus.isProto1Enabled() && mDesktopModeController.isPresent() - && mDesktopModeController.get().getDisplayAreaWindowingMode(taskInfo.displayId) - == WINDOWING_MODE_FULLSCREEN) { - return false; - } if (mGestureDetector.onTouchEvent(e)) { return true; } @@ -540,6 +612,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { super.dispose(); } + @Override + public String toString() { + return "EventReceiver" + + "{" + + "tasksOnDisplay=" + + mTasksOnDisplay + + "}"; + } + private void incrementTaskNumber() { mTasksOnDisplay++; } @@ -585,23 +666,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { */ private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) { final DesktopModeWindowDecoration relevantDecor = getRelevantWindowDecor(ev); - if (DesktopModeStatus.isProto2Enabled()) { - if (relevantDecor == null + if (DesktopModeStatus.isEnabled()) { + if (!mInImmersiveMode && (relevantDecor == null || relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM - || mTransitionDragActive) { + || mTransitionDragActive)) { handleCaptionThroughStatusBar(ev, relevantDecor); } } handleEventOutsideFocusedCaption(ev, relevantDecor); // Prevent status bar from reacting to a caption drag. - if (DesktopModeStatus.isProto2Enabled()) { + if (DesktopModeStatus.isEnabled()) { if (mTransitionDragActive) { inputMonitor.pilferPointers(); } - } else if (DesktopModeStatus.isProto1Enabled()) { - if (mTransitionDragActive && !DesktopModeStatus.isActive(mContext)) { - inputMonitor.pilferPointers(); - } } } @@ -634,7 +711,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDragToDesktopAnimationStartBounds.set( relevantDecor.mTaskInfo.configuration.windowConfiguration.getBounds()); boolean dragFromStatusBarAllowed = false; - if (DesktopModeStatus.isProto2Enabled()) { + if (DesktopModeStatus.isEnabled()) { // In proto2 any full screen or multi-window task can be dragged to // freeform. final int windowingMode = relevantDecor.mTaskInfo.getWindowingMode(); @@ -659,10 +736,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final int statusBarHeight = getStatusBarHeight( relevantDecor.mTaskInfo.displayId); if (ev.getY() > 2 * statusBarHeight) { - if (DesktopModeStatus.isProto2Enabled()) { + if (DesktopModeStatus.isEnabled()) { animateToDesktop(relevantDecor, ev); - } else if (DesktopModeStatus.isProto1Enabled()) { - mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); } mMoveToDesktopAnimator = null; return; @@ -692,6 +767,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { relevantDecor.mTaskInfo.displayId); if (ev.getY() > statusBarHeight) { if (mMoveToDesktopAnimator == null) { + closeOtherSplitTask(relevantDecor.mTaskInfo.taskId); mMoveToDesktopAnimator = new MoveToDesktopAnimator( mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo, relevantDecor.mTaskSurface); @@ -782,7 +858,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) { if (mSplitScreenController != null && mSplitScreenController.isSplitScreenVisible()) { // We can't look at focused task here as only one task will have focus. - return getSplitScreenDecor(ev); + DesktopModeWindowDecoration splitTaskDecor = getSplitScreenDecor(ev); + return splitTaskDecor == null ? getFocusedDecor() : splitTaskDecor; } else { return getFocusedDecor(); } @@ -853,7 +930,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && taskInfo.isFocused) { return false; } - return DesktopModeStatus.isProto2Enabled() + return DesktopModeStatus.isEnabled() && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop() @@ -880,7 +957,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { taskSurface, mMainHandler, mMainChoreographer, - mSyncQueue); + mSyncQueue, + mRootTaskDisplayAreaOrganizer); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); windowDecoration.createResizeVeil(); @@ -889,8 +967,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final DesktopModeTouchEventListener touchEventListener = new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback); - windowDecoration.setCaptionListeners(touchEventListener, touchEventListener); - windowDecoration.setCornersListener(mCornersListener); + windowDecoration.setCaptionListeners( + touchEventListener, touchEventListener, touchEventListener); + windowDecoration.setExclusionRegionListener(mExclusionRegionListener); windowDecoration.setDragPositioningCallback(dragPositioningCallback); windowDecoration.setDragDetector(touchEventListener.mDragDetector); windowDecoration.relayout(taskInfo, startT, finishT, @@ -912,11 +991,40 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } } + private RunningTaskInfo getOtherSplitTask(int taskId) { + @SplitPosition int remainingTaskPosition = mSplitScreenController + .getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT + ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT; + return mSplitScreenController.getTaskInfo(remainingTaskPosition); + } + + private void closeOtherSplitTask(int taskId) { + if (isTaskInSplitScreen(taskId)) { + mTaskOperations.closeTask(getOtherSplitTask(taskId).token); + } + } + + private boolean isTaskInSplitScreen(int taskId) { + return mSplitScreenController != null + && mSplitScreenController.isTaskInSplitScreen(taskId); + } + + private void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + "DesktopModeWindowDecorViewModel"); + pw.println(innerPrefix + "DesktopModeStatus=" + DesktopModeStatus.isEnabled()); + pw.println(innerPrefix + "mTransitionDragActive=" + mTransitionDragActive); + pw.println(innerPrefix + "mEventReceiversByDisplay=" + mEventReceiversByDisplay); + pw.println(innerPrefix + "mWindowDecorByTaskId=" + mWindowDecorByTaskId); + } + private class DragStartListenerImpl implements DragPositioningCallbackUtility.DragStartListener { @Override public void onDragStart(int taskId) { - mWindowDecorByTaskId.get(taskId).closeHandleMenu(); + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId); + decoration.closeHandleMenu(); + decoration.closeMaximizeMenu(); } } @@ -926,19 +1034,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } } - private class TaskCornersListenerImpl - implements DesktopModeWindowDecoration.TaskCornersListener { + private class ExclusionRegionListenerImpl + implements ExclusionRegionListener { @Override - public void onTaskCornersChanged(int taskId, Region corner) { - mDesktopModeController.ifPresent(d -> d.onTaskCornersChanged(taskId, corner)); - mDesktopTasksController.ifPresent(d -> d.onTaskCornersChanged(taskId, corner)); + public void onExclusionRegionChanged(int taskId, Region region) { + mDesktopTasksController.ifPresent(d -> d.onExclusionRegionChanged(taskId, region)); } @Override - public void onTaskCornersRemoved(int taskId) { - mDesktopModeController.ifPresent(d -> d.removeCornersForTask(taskId)); - mDesktopTasksController.ifPresent(d -> d.removeCornersForTask(taskId)); + public void onExclusionRegionDismissed(int taskId) { + mDesktopTasksController.ifPresent(d -> d.removeExclusionRegionForTask(taskId)); } } @@ -957,6 +1063,35 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return mIsKeyguardVisible && mIsKeyguardOccluded; } } + + @VisibleForTesting + class DesktopModeOnInsetsChangedListener implements + DisplayInsetsController.OnInsetsChangedListener { + @Override + public void insetsChanged(InsetsState insetsState) { + for (int i = 0; i < insetsState.sourceSize(); i++) { + final InsetsSource source = insetsState.sourceAt(i); + if (source.getType() != statusBars()) { + continue; + } + + final DesktopModeWindowDecoration decor = getFocusedDecor(); + if (decor == null) { + return; + } + // If status bar inset is visible, top task is not in immersive mode + final boolean inImmersiveMode = !source.isVisible(); + // Calls WindowDecoration#relayout if decoration visibility needs to be updated + if (inImmersiveMode != mInImmersiveMode) { + decor.relayout(decor.mTaskInfo); + mInImmersiveMode = inImmersiveMode; + } + + return; + } + } + } + } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index a359395711e3..248e83747c48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -17,12 +17,16 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.windowingModeToString; import android.app.ActivityManager; +import android.app.WindowConfiguration.WindowingMode; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -36,13 +40,16 @@ import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.View; import android.view.ViewConfiguration; +import android.widget.ImageButton; import android.window.WindowContainerTransaction; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; @@ -52,6 +59,7 @@ import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationVi import java.util.HashSet; import java.util.Set; +import java.util.function.Supplier; /** * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with @@ -69,6 +77,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private DesktopModeWindowDecorationViewHolder mWindowDecorViewHolder; private View.OnClickListener mOnCaptionButtonClickListener; private View.OnTouchListener mOnCaptionTouchListener; + private View.OnLongClickListener mOnCaptionLongClickListener; private DragPositioningCallback mDragPositioningCallback; private DragResizeInputListener mDragResizeListener; private DragDetector mDragDetector; @@ -80,15 +89,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private final Point mPositionInParent = new Point(); private HandleMenu mHandleMenu; + private MaximizeMenu mMaximizeMenu; + private ResizeVeil mResizeVeil; private Drawable mAppIcon; private CharSequence mAppName; - private TaskCornersListener mCornersListener; + private ExclusionRegionListener mExclusionRegionListener; private final Set<IBinder> mTransitionsPausingRelayout = new HashSet<>(); private int mRelayoutBlock; + private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; DesktopModeWindowDecoration( Context context, @@ -96,38 +108,55 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, + Configuration windowDecorConfig, Handler handler, Choreographer choreographer, - SyncTransactionQueue syncQueue) { - super(context, displayController, taskOrganizer, taskInfo, taskSurface); + SyncTransactionQueue syncQueue, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { + this (context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig, + handler, choreographer, syncQueue, rootTaskDisplayAreaOrganizer, + SurfaceControl.Builder::new, SurfaceControl.Transaction::new, + WindowContainerTransaction::new, new SurfaceControlViewHostFactory() {}); + } + + DesktopModeWindowDecoration( + Context context, + DisplayController displayController, + ShellTaskOrganizer taskOrganizer, + ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl taskSurface, + Configuration windowDecorConfig, + Handler handler, + Choreographer choreographer, + SyncTransactionQueue syncQueue, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, + Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, + Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, + SurfaceControlViewHostFactory surfaceControlViewHostFactory) { + super(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig, + surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, + windowContainerTransactionSupplier, surfaceControlViewHostFactory); mHandler = handler; mChoreographer = choreographer; mSyncQueue = syncQueue; + mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; loadAppInfo(); } - @Override - protected Configuration getConfigurationWithOverrides( - ActivityManager.RunningTaskInfo taskInfo) { - Configuration configuration = taskInfo.getConfiguration(); - if (DesktopTasksController.isDesktopDensityOverrideSet()) { - // Density is overridden for desktop tasks. Keep system density for window decoration. - configuration.densityDpi = mContext.getResources().getConfiguration().densityDpi; - } - return configuration; - } - void setCaptionListeners( View.OnClickListener onCaptionButtonClickListener, - View.OnTouchListener onCaptionTouchListener) { + View.OnTouchListener onCaptionTouchListener, + View.OnLongClickListener onLongClickListener) { mOnCaptionButtonClickListener = onCaptionButtonClickListener; mOnCaptionTouchListener = onCaptionTouchListener; + mOnCaptionLongClickListener = onLongClickListener; } - void setCornersListener(TaskCornersListener cornersListener) { - mCornersListener = cornersListener; + void setExclusionRegionListener(ExclusionRegionListener exclusionRegionListener) { + mExclusionRegionListener = exclusionRegionListener; } void setDragPositioningCallback(DragPositioningCallback dragPositioningCallback) { @@ -148,7 +177,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin return; } - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is // synced with the buffer transaction (that draws the View). Both will be shown on screen // at the same, whereas applying them independently causes flickering. See b/270202228. @@ -178,9 +207,23 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mRelayoutParams.reset(); mRelayoutParams.mRunningTaskInfo = taskInfo; mRelayoutParams.mLayoutResId = windowDecorLayoutId; - mRelayoutParams.mCaptionHeightId = getCaptionHeightId(); + mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode()); mRelayoutParams.mShadowRadiusId = shadowRadiusID; mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; + // The configuration used to lay out the window decoration. The system context's config is + // used when the task density has been overridden to a custom density so that the resources + // and views of the decoration aren't affected and match the rest of the System UI, if not + // then just use the task's configuration. A copy is made instead of using the original + // reference so that the configuration isn't mutated on config changes and diff checks can + // be made in WindowDecoration#relayout using the pre/post-relayout configuration. + // See b/301119301. + // TODO(b/301119301): consider moving the config data needed for diffs to relayout params + // instead of using a whole Configuration as a parameter. + final Configuration windowDecorConfig = new Configuration(); + windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet() + ? mContext.getResources().getConfiguration() // Use system context. + : mTaskInfo.configuration); // Use task configuration. + mRelayoutParams.mWindowDecorConfig = windowDecorConfig; mRelayoutParams.mCornerRadius = (int) ScreenDecorationsUtils.getWindowCornerRadius(mContext); @@ -207,6 +250,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mResult.mRootView, mOnCaptionTouchListener, mOnCaptionButtonClickListener, + mOnCaptionLongClickListener, mAppName, mAppIcon ); @@ -218,9 +262,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin if (!mTaskInfo.isFocused) { closeHandleMenu(); + closeMaximizeMenu(); } if (!isDragResizeable) { + if (!mTaskInfo.positionInParent.equals(mPositionInParent)) { + // We still want to track caption bar's exclusion region on a non-resizeable task. + updateExclusionRegion(); + } closeDragResizeListener(); return; } @@ -248,13 +297,58 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin final int resize_corner = mResult.mRootView.getResources() .getDimensionPixelSize(R.dimen.freeform_resize_corner); - // If either task geometry or position have changed, update this task's cornersListener + // If either task geometry or position have changed, update this task's + // exclusion region listener if (mDragResizeListener.setGeometry( mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop) || !mTaskInfo.positionInParent.equals(mPositionInParent)) { - mCornersListener.onTaskCornersChanged(mTaskInfo.taskId, getGlobalCornersRegion()); + updateExclusionRegion(); + } + + if (isMaximizeMenuActive()) { + if (!mTaskInfo.isVisible()) { + closeMaximizeMenu(); + } else { + mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT); + } } - mPositionInParent.set(mTaskInfo.positionInParent); + } + + private PointF calculateMaximizeMenuPosition() { + final PointF position = new PointF(); + final Resources resources = mContext.getResources(); + final DisplayLayout displayLayout = + mDisplayController.getDisplayLayout(mTaskInfo.displayId); + if (displayLayout == null) return position; + + final int displayWidth = displayLayout.width(); + final int displayHeight = displayLayout.height(); + final int captionHeight = getCaptionHeight(mTaskInfo.getWindowingMode()); + + final ImageButton maximizeWindowButton = + mResult.mRootView.findViewById(R.id.maximize_window); + final int[] maximizeButtonLocation = new int[2]; + maximizeWindowButton.getLocationInWindow(maximizeButtonLocation); + + final int menuWidth = loadDimensionPixelSize( + resources, R.dimen.desktop_mode_maximize_menu_width); + final int menuHeight = loadDimensionPixelSize( + resources, R.dimen.desktop_mode_maximize_menu_height); + + float menuLeft = (mPositionInParent.x + maximizeButtonLocation[0]); + float menuTop = (mPositionInParent.y + captionHeight); + final float menuRight = menuLeft + menuWidth; + final float menuBottom = menuTop + menuHeight; + + // If the menu is out of screen bounds, shift it up/left as needed + if (menuRight > displayWidth) { + menuLeft = (displayWidth - menuWidth); + } + if (menuBottom > displayHeight) { + menuTop = (displayHeight - menuHeight); + } + + return new PointF(menuLeft, menuTop); } boolean isHandleMenuActive() { @@ -335,6 +429,29 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } /** + * Create and display maximize menu window + */ + void createMaximizeMenu() { + mMaximizeMenu = new MaximizeMenu(mSyncQueue, mRootTaskDisplayAreaOrganizer, + mDisplayController, mTaskInfo, mOnCaptionButtonClickListener, mContext, + calculateMaximizeMenuPosition(), mSurfaceControlTransactionSupplier); + mMaximizeMenu.show(); + } + + /** + * Close the maximize menu window + */ + void closeMaximizeMenu() { + if (!isMaximizeMenuActive()) return; + mMaximizeMenu.close(); + mMaximizeMenu = null; + } + + boolean isMaximizeMenuActive() { + return mMaximizeMenu != null; + } + + /** * Create and display handle menu window */ void createHandleMenu() { @@ -345,7 +462,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin .setOnTouchListener(mOnCaptionTouchListener) .setLayoutId(mRelayoutParams.mLayoutResId) .setCaptionPosition(mRelayoutParams.mCaptionX, mRelayoutParams.mCaptionY) - .setWindowingButtonsVisible(DesktopModeStatus.isProto2Enabled()) + .setWindowingButtonsVisible(DesktopModeStatus.isEnabled()) .build(); mHandleMenu.show(); } @@ -362,6 +479,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @Override void releaseViews() { closeHandleMenu(); + closeMaximizeMenu(); super.releaseViews(); } @@ -460,27 +578,43 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin public void close() { closeDragResizeListener(); closeHandleMenu(); - mCornersListener.onTaskCornersRemoved(mTaskInfo.taskId); + mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId); disposeResizeVeil(); super.close(); } - private int getDesktopModeWindowDecorLayoutId(int windowingMode) { - if (DesktopModeStatus.isProto1Enabled()) { - return R.layout.desktop_mode_app_controls_window_decor; - } + private int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) { return windowingMode == WINDOWING_MODE_FREEFORM ? R.layout.desktop_mode_app_controls_window_decor : R.layout.desktop_mode_focused_window_decor; } + private void updatePositionInParent() { + mPositionInParent.set(mTaskInfo.positionInParent); + } + + private void updateExclusionRegion() { + // An outdated position in parent is one reason for this to be called; update it here. + updatePositionInParent(); + mExclusionRegionListener + .onExclusionRegionChanged(mTaskInfo.taskId, getGlobalExclusionRegion()); + } + /** - * Create a new region out of the corner rects of this task. + * Create a new exclusion region from the corner rects (if resizeable) and caption bounds + * of this task. */ - Region getGlobalCornersRegion() { - Region cornersRegion = mDragResizeListener.getCornersRegion(); - cornersRegion.translate(mPositionInParent.x, mPositionInParent.y); - return cornersRegion; + private Region getGlobalExclusionRegion() { + Region exclusionRegion; + if (mTaskInfo.isResizeable) { + exclusionRegion = mDragResizeListener.getCornersRegion(); + } else { + exclusionRegion = new Region(); + } + exclusionRegion.union(new Rect(0, 0, mResult.mWidth, + getCaptionHeight(mTaskInfo.getWindowingMode()))); + exclusionRegion.translate(mPositionInParent.x, mPositionInParent.y); + return exclusionRegion; } /** @@ -494,8 +628,19 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } @Override - int getCaptionHeightId() { - return R.dimen.freeform_decor_caption_height; + int getCaptionHeightId(@WindowingMode int windowingMode) { + return windowingMode == WINDOWING_MODE_FULLSCREEN + ? R.dimen.desktop_mode_fullscreen_decor_caption_height + : R.dimen.desktop_mode_freeform_decor_caption_height; + } + + private int getCaptionHeight(@WindowingMode int windowingMode) { + return loadDimensionPixelSize(mContext.getResources(), getCaptionHeightId(windowingMode)); + } + + @Override + int getCaptionViewId() { + return R.id.desktop_mode_caption; } /** @@ -522,6 +667,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mRelayoutBlock++; } + @Override + public String toString() { + return "{" + + "mPositionInParent=" + mPositionInParent + ", " + + "mRelayoutBlock=" + mRelayoutBlock + ", " + + "taskId=" + mTaskInfo.taskId + ", " + + "windowingMode=" + windowingModeToString(mTaskInfo.getWindowingMode()) + ", " + + "isFocused=" + isFocused() + + "}"; + } + static class Factory { DesktopModeWindowDecoration create( @@ -532,25 +688,34 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin SurfaceControl taskSurface, Handler handler, Choreographer choreographer, - SyncTransactionQueue syncQueue) { + SyncTransactionQueue syncQueue, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { + final Configuration windowDecorConfig = + DesktopTasksController.isDesktopDensityOverrideSet() + ? context.getResources().getConfiguration() // Use system context + : taskInfo.configuration; // Use task configuration return new DesktopModeWindowDecoration( context, displayController, taskOrganizer, taskInfo, taskSurface, + windowDecorConfig, handler, choreographer, - syncQueue); + syncQueue, + rootTaskDisplayAreaOrganizer); } } - interface TaskCornersListener { - /** Inform the implementing class of this task's change in corner resize handles */ - void onTaskCornersChanged(int taskId, Region corner); + interface ExclusionRegionListener { + /** Inform the implementing class of this task's change in region resize handles */ + void onExclusionRegionChanged(int taskId, Region region); - /** Inform the implementing class that this task no longer needs its corners tracked, - * likely due to it closing. */ - void onTaskCornersRemoved(int taskId); + /** + * Inform the implementing class that this task no longer needs an exclusion region, + * likely due to it closing. + */ + void onExclusionRegionDismissed(int taskId); } } 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 e32bd42acf74..cb0a6c733fe3 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 @@ -143,6 +143,24 @@ public class DragPositioningCallbackUtility { } /** + * Calculates the new position of the top edge of the task and returns true if it is below the + * disallowed area. + * + * @param disallowedAreaForEndBoundsHeight the height of the area that where the task positioner + * should not finalize the bounds using WCT#setBounds + * @param taskBoundsAtDragStart the bounds of the task on the first drag input event + * @param repositionStartPoint initial input coordinate + * @param y the y position of the motion event + * @return true if the top of the task is below the disallowed area + */ + static boolean isBelowDisallowedArea(int disallowedAreaForEndBoundsHeight, + Rect taskBoundsAtDragStart, PointF repositionStartPoint, float y) { + final float deltaY = y - repositionStartPoint.y; + final float topPosition = taskBoundsAtDragStart.top + deltaY; + return topPosition > disallowedAreaForEndBoundsHeight; + } + + /** * Updates repositionTaskBounds to the final bounds of the task after the drag is finished. If * the bounds are outside of the stable bounds, they are shifted to place task at the top of the * stable bounds. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java index e0ee25242550..dadd264596fb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java @@ -18,6 +18,7 @@ package com.android.wm.shell.windowdecor; import android.graphics.PointF; import android.graphics.Rect; +import android.view.Surface; import android.view.SurfaceControl; import android.window.WindowContainerTransaction; @@ -45,6 +46,7 @@ class FluidResizeTaskPositioner implements DragPositioningCallback { private final int mDisallowedAreaForEndBoundsHeight; private boolean mHasDragResized; private int mCtrlType; + @Surface.Rotation private int mRotation; FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration, DisplayController displayController, int disallowedAreaForEndBoundsHeight) { @@ -78,7 +80,10 @@ class FluidResizeTaskPositioner implements DragPositioningCallback { mTaskOrganizer.applyTransaction(wct); } mRepositionTaskBounds.set(mTaskBoundsAtDragStart); - if (mStableBounds.isEmpty()) { + int rotation = mWindowDecoration + .mTaskInfo.configuration.windowConfiguration.getDisplayRotation(); + if (mStableBounds.isEmpty() || mRotation != rotation) { + mRotation = rotation; mDisplayController.getDisplayLayout(mWindowDecoration.mDisplay.getDisplayId()) .getStableBounds(mStableBounds); } @@ -125,7 +130,9 @@ class FluidResizeTaskPositioner implements DragPositioningCallback { } mTaskOrganizer.applyTransaction(wct); } else if (mCtrlType == CTRL_TYPE_UNDEFINED - && y > mDisallowedAreaForEndBoundsHeight) { + && DragPositioningCallbackUtility.isBelowDisallowedArea( + mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint, + y)) { final WindowContainerTransaction wct = new WindowContainerTransaction(); DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java index ac4a597c15d1..15f8f1cfadf2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java @@ -26,7 +26,10 @@ import android.annotation.Nullable; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.content.res.ColorStateList; +import android.content.res.Configuration; import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; import android.graphics.PointF; import android.graphics.drawable.Drawable; import android.view.MotionEvent; @@ -39,7 +42,6 @@ import android.widget.TextView; import android.window.SurfaceSyncGroup; import com.android.wm.shell.R; -import com.android.wm.shell.desktopmode.DesktopModeStatus; /** * Handle menu opened when the appropriate button is clicked on. @@ -53,12 +55,8 @@ class HandleMenu { private static final String TAG = "HandleMenu"; private final Context mContext; private final WindowDecoration mParentDecor; - private WindowDecoration.AdditionalWindow mAppInfoPill; - private WindowDecoration.AdditionalWindow mWindowingPill; - private WindowDecoration.AdditionalWindow mMoreActionsPill; - private final PointF mAppInfoPillPosition = new PointF(); - private final PointF mWindowingPillPosition = new PointF(); - private final PointF mMoreActionsPillPosition = new PointF(); + private WindowDecoration.AdditionalWindow mHandleMenuWindow; + private final PointF mHandleMenuPosition = new PointF(); private final boolean mShouldShowWindowingPill; private final Drawable mAppIcon; private final CharSequence mAppName; @@ -70,13 +68,8 @@ class HandleMenu { private final int mCaptionY; private int mMarginMenuTop; private int mMarginMenuStart; - private int mMarginMenuSpacing; + private int mMenuHeight; private int mMenuWidth; - private int mAppInfoPillHeight; - private int mWindowingPillHeight; - private int mMoreActionsPillHeight; - private int mShadowRadius; - private int mCornerRadius; HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY, @@ -101,98 +94,110 @@ class HandleMenu { final SurfaceSyncGroup ssg = new SurfaceSyncGroup(TAG); SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - createAppInfoPill(t, ssg); - if (mShouldShowWindowingPill) { - createWindowingPill(t, ssg); - } - createMoreActionsPill(t, ssg); + createHandleMenuWindow(t, ssg); ssg.addTransaction(t); ssg.markSyncReady(); setupHandleMenu(); } - private void createAppInfoPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { - final int x = (int) mAppInfoPillPosition.x; - final int y = (int) mAppInfoPillPosition.y; - mAppInfoPill = mParentDecor.addWindow( - R.layout.desktop_mode_window_decor_handle_menu_app_info_pill, - "Menu's app info pill", - t, ssg, x, y, mMenuWidth, mAppInfoPillHeight, mShadowRadius, mCornerRadius); + private void createHandleMenuWindow(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { + final int x = (int) mHandleMenuPosition.x; + final int y = (int) mHandleMenuPosition.y; + mHandleMenuWindow = mParentDecor.addWindow( + R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu", + t, ssg, x, y, mMenuWidth, mMenuHeight); } - private void createWindowingPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { - final int x = (int) mWindowingPillPosition.x; - final int y = (int) mWindowingPillPosition.y; - mWindowingPill = mParentDecor.addWindow( - R.layout.desktop_mode_window_decor_handle_menu_windowing_pill, - "Menu's windowing pill", - t, ssg, x, y, mMenuWidth, mWindowingPillHeight, mShadowRadius, mCornerRadius); - } - - private void createMoreActionsPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { - final int x = (int) mMoreActionsPillPosition.x; - final int y = (int) mMoreActionsPillPosition.y; - mMoreActionsPill = mParentDecor.addWindow( - R.layout.desktop_mode_window_decor_handle_menu_more_actions_pill, - "Menu's more actions pill", - t, ssg, x, y, mMenuWidth, mMoreActionsPillHeight, mShadowRadius, mCornerRadius); + /** + * Set up all three pills of the handle menu: app info pill, windowing pill, & more actions + * pill. + */ + private void setupHandleMenu() { + final View handleMenu = mHandleMenuWindow.mWindowViewHost.getView(); + handleMenu.setOnTouchListener(mOnTouchListener); + setupAppInfoPill(handleMenu); + if (mShouldShowWindowingPill) { + setupWindowingPill(handleMenu); + } + setupMoreActionsPill(handleMenu); } /** - * Set up interactive elements and color of this handle menu + * Set up interactive elements of handle menu's app info pill. */ - private void setupHandleMenu() { - // App Info pill setup. - final View appInfoPillView = mAppInfoPill.mWindowViewHost.getView(); - final ImageButton collapseBtn = appInfoPillView.findViewById(R.id.collapse_menu_button); - final ImageView appIcon = appInfoPillView.findViewById(R.id.application_icon); - final TextView appName = appInfoPillView.findViewById(R.id.application_name); + private void setupAppInfoPill(View handleMenu) { + final ImageButton collapseBtn = handleMenu.findViewById(R.id.collapse_menu_button); + final ImageView appIcon = handleMenu.findViewById(R.id.application_icon); + final TextView appName = handleMenu.findViewById(R.id.application_name); collapseBtn.setOnClickListener(mOnClickListener); - appInfoPillView.setOnTouchListener(mOnTouchListener); appIcon.setImageDrawable(mAppIcon); appName.setText(mAppName); + } - // Windowing pill setup. - if (mShouldShowWindowingPill) { - final View windowingPillView = mWindowingPill.mWindowViewHost.getView(); - final ImageButton fullscreenBtn = windowingPillView.findViewById( - R.id.fullscreen_button); - final ImageButton splitscreenBtn = windowingPillView.findViewById( - R.id.split_screen_button); - final ImageButton floatingBtn = windowingPillView.findViewById(R.id.floating_button); - final ImageButton desktopBtn = windowingPillView.findViewById(R.id.desktop_button); - fullscreenBtn.setOnClickListener(mOnClickListener); - splitscreenBtn.setOnClickListener(mOnClickListener); - floatingBtn.setOnClickListener(mOnClickListener); - desktopBtn.setOnClickListener(mOnClickListener); - // The button corresponding to the windowing mode that the task is currently in uses a - // different color than the others. - final ColorStateList activeColorStateList = ColorStateList.valueOf( - mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_active)); - final ColorStateList inActiveColorStateList = ColorStateList.valueOf( - mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_inactive)); - fullscreenBtn.setImageTintList( - mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN - ? activeColorStateList : inActiveColorStateList); - splitscreenBtn.setImageTintList( - mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW - ? activeColorStateList : inActiveColorStateList); - floatingBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_PINNED - ? activeColorStateList : inActiveColorStateList); - desktopBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM - ? activeColorStateList : inActiveColorStateList); - } + /** + * Set up interactive elements and color of handle menu's windowing pill. + */ + private void setupWindowingPill(View handleMenu) { + final ImageButton fullscreenBtn = handleMenu.findViewById( + R.id.fullscreen_button); + final ImageButton splitscreenBtn = handleMenu.findViewById( + R.id.split_screen_button); + final ImageButton floatingBtn = handleMenu.findViewById(R.id.floating_button); + // TODO: Remove once implemented. + floatingBtn.setVisibility(View.GONE); + + final ImageButton desktopBtn = handleMenu.findViewById(R.id.desktop_button); + fullscreenBtn.setOnClickListener(mOnClickListener); + splitscreenBtn.setOnClickListener(mOnClickListener); + floatingBtn.setOnClickListener(mOnClickListener); + desktopBtn.setOnClickListener(mOnClickListener); + // The button corresponding to the windowing mode that the task is currently in uses a + // different color than the others. + final ColorStateList[] iconColors = getWindowingIconColor(); + final ColorStateList inActiveColorStateList = iconColors[0]; + final ColorStateList activeColorStateList = iconColors[1]; + final int windowingMode = mTaskInfo.getWindowingMode(); + fullscreenBtn.setImageTintList(windowingMode == WINDOWING_MODE_FULLSCREEN + ? activeColorStateList : inActiveColorStateList); + splitscreenBtn.setImageTintList(windowingMode == WINDOWING_MODE_MULTI_WINDOW + ? activeColorStateList : inActiveColorStateList); + floatingBtn.setImageTintList(windowingMode == WINDOWING_MODE_PINNED + ? activeColorStateList : inActiveColorStateList); + desktopBtn.setImageTintList(windowingMode == WINDOWING_MODE_FREEFORM + ? activeColorStateList : inActiveColorStateList); + } - // More Actions pill setup. - final View moreActionsPillView = mMoreActionsPill.mWindowViewHost.getView(); - final Button closeBtn = moreActionsPillView.findViewById(R.id.close_button); - closeBtn.setOnClickListener(mOnClickListener); - final Button selectBtn = moreActionsPillView.findViewById(R.id.select_button); + /** + * Set up interactive elements & height of handle menu's more actions pill + */ + private void setupMoreActionsPill(View handleMenu) { + final Button selectBtn = handleMenu.findViewById(R.id.select_button); selectBtn.setOnClickListener(mOnClickListener); + final Button screenshotBtn = handleMenu.findViewById(R.id.screenshot_button); + // TODO: Remove once implemented. + screenshotBtn.setVisibility(View.GONE); } /** - * Updates the handle menu pills' position variables to reflect their next positions + * Returns array of windowing icon color based on current UI theme. First element of the + * array is for inactive icons and the second is for active icons. + */ + private ColorStateList[] getWindowingIconColor() { + final int mode = mContext.getResources().getConfiguration().uiMode + & Configuration.UI_MODE_NIGHT_MASK; + final boolean isNightMode = (mode == Configuration.UI_MODE_NIGHT_YES); + final TypedArray typedArray = mContext.obtainStyledAttributes(new int[]{ + com.android.internal.R.attr.materialColorOnSurface, + com.android.internal.R.attr.materialColorPrimary}); + final int inActiveColor = typedArray.getColor(0, isNightMode ? Color.WHITE : Color.BLACK); + final int activeColor = typedArray.getColor(1, isNightMode ? Color.WHITE : Color.BLACK); + typedArray.recycle(); + return new ColorStateList[]{ColorStateList.valueOf(inActiveColor), + ColorStateList.valueOf(activeColor)}; + } + + /** + * Updates handle menu's position variables to reflect its next position. */ private void updateHandleMenuPillPositions() { final int menuX, menuY; @@ -209,55 +214,36 @@ class HandleMenu { menuY = mCaptionY + mMarginMenuStart; } - // App Info pill setup. - final int appInfoPillY = menuY; - mAppInfoPillPosition.set(menuX, appInfoPillY); + // Handle Menu position setup. + mHandleMenuPosition.set(menuX, menuY); - final int windowingPillY, moreActionsPillY; - if (mShouldShowWindowingPill) { - windowingPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing; - mWindowingPillPosition.set(menuX, windowingPillY); - moreActionsPillY = windowingPillY + mWindowingPillHeight + mMarginMenuSpacing; - mMoreActionsPillPosition.set(menuX, moreActionsPillY); - } else { - // Just start after the end of the app info pill + margins. - moreActionsPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing; - mMoreActionsPillPosition.set(menuX, moreActionsPillY); - } } /** * Update pill layout, in case task changes have caused positioning to change. - * @param t */ void relayout(SurfaceControl.Transaction t) { - if (mAppInfoPill != null) { + if (mHandleMenuWindow != null) { updateHandleMenuPillPositions(); - t.setPosition(mAppInfoPill.mWindowSurface, - mAppInfoPillPosition.x, mAppInfoPillPosition.y); - // Only show windowing buttons in proto2. Proto1 uses a system-level mode only. - final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled(); - if (shouldShowWindowingPill) { - t.setPosition(mWindowingPill.mWindowSurface, - mWindowingPillPosition.x, mWindowingPillPosition.y); - } - t.setPosition(mMoreActionsPill.mWindowSurface, - mMoreActionsPillPosition.x, mMoreActionsPillPosition.y); + t.setPosition(mHandleMenuWindow.mWindowSurface, + mHandleMenuPosition.x, mHandleMenuPosition.y); } } + /** * Check a passed MotionEvent if a click has occurred on any button on this caption * Note this should only be called when a regular onClick is not possible * (i.e. the button was clicked through status bar layer) + * * @param ev the MotionEvent to compare against. */ void checkClickEvent(MotionEvent ev) { - final View appInfoPill = mAppInfoPill.mWindowViewHost.getView(); - final ImageButton collapse = appInfoPill.findViewById(R.id.collapse_menu_button); + final View handleMenu = mHandleMenuWindow.mWindowViewHost.getView(); + final ImageButton collapse = handleMenu.findViewById(R.id.collapse_menu_button); // Translate the input point from display coordinates to the same space as the collapse // button, meaning its parent (app info pill view). - final PointF inputPoint = new PointF(ev.getX() - mAppInfoPillPosition.x, - ev.getY() - mAppInfoPillPosition.y); + final PointF inputPoint = new PointF(ev.getX() - mHandleMenuPosition.x, + ev.getY() - mHandleMenuPosition.y); if (pointInView(collapse, inputPoint.x, inputPoint.y)) { mOnClickListener.onClick(collapse); } @@ -267,27 +253,15 @@ class HandleMenu { * A valid menu input is one of the following: * An input that happens in the menu views. * Any input before the views have been laid out. + * * @param inputPoint the input to compare against. */ boolean isValidMenuInput(PointF inputPoint) { if (!viewsLaidOut()) return true; - final boolean pointInAppInfoPill = pointInView( - mAppInfoPill.mWindowViewHost.getView(), - inputPoint.x - mAppInfoPillPosition.x, - inputPoint.y - mAppInfoPillPosition.y); - boolean pointInWindowingPill = false; - if (mWindowingPill != null) { - pointInWindowingPill = pointInView( - mWindowingPill.mWindowViewHost.getView(), - inputPoint.x - mWindowingPillPosition.x, - inputPoint.y - mWindowingPillPosition.y); - } - final boolean pointInMoreActionsPill = pointInView( - mMoreActionsPill.mWindowViewHost.getView(), - inputPoint.x - mMoreActionsPillPosition.x, - inputPoint.y - mMoreActionsPillPosition.y); - - return pointInAppInfoPill || pointInWindowingPill || pointInMoreActionsPill; + return pointInView( + mHandleMenuWindow.mWindowViewHost.getView(), + inputPoint.x - mHandleMenuPosition.x, + inputPoint.y - mHandleMenuPosition.y); } private boolean pointInView(View v, float x, float y) { @@ -297,33 +271,33 @@ class HandleMenu { /** * Check if the views for handle menu can be seen. - * @return */ private boolean viewsLaidOut() { - return mAppInfoPill.mWindowViewHost.getView().isLaidOut(); + return mHandleMenuWindow.mWindowViewHost.getView().isLaidOut(); } - private void loadHandleMenuDimensions() { final Resources resources = mContext.getResources(); mMenuWidth = loadDimensionPixelSize(resources, R.dimen.desktop_mode_handle_menu_width); + mMenuHeight = getHandleMenuHeight(resources); mMarginMenuTop = loadDimensionPixelSize(resources, R.dimen.desktop_mode_handle_menu_margin_top); mMarginMenuStart = loadDimensionPixelSize(resources, R.dimen.desktop_mode_handle_menu_margin_start); - mMarginMenuSpacing = loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_pill_spacing_margin); - mAppInfoPillHeight = loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_app_info_pill_height); - mWindowingPillHeight = loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_windowing_pill_height); - mMoreActionsPillHeight = loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_more_actions_pill_height); - mShadowRadius = loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_shadow_radius); - mCornerRadius = loadDimensionPixelSize(resources, - R.dimen.desktop_mode_handle_menu_corner_radius); + } + + /** + * Determines handle menu height based on if windowing pill should be shown. + */ + private int getHandleMenuHeight(Resources resources) { + int menuHeight = loadDimensionPixelSize(resources, + R.dimen.desktop_mode_handle_menu_height); + if (!mShouldShowWindowingPill) { + menuHeight -= loadDimensionPixelSize(resources, + R.dimen.desktop_mode_handle_menu_windowing_pill_height); + } + return menuHeight; } private int loadDimensionPixelSize(Resources resources, int resourceId) { @@ -334,14 +308,8 @@ class HandleMenu { } void close() { - mAppInfoPill.releaseView(); - mAppInfoPill = null; - if (mWindowingPill != null) { - mWindowingPill.releaseView(); - mWindowingPill = null; - } - mMoreActionsPill.releaseView(); - mMoreActionsPill = null; + mHandleMenuWindow.releaseView(); + mHandleMenuWindow = null; } static final class Builder { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt new file mode 100644 index 000000000000..050d1e9df392 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.windowdecor + +import android.app.ActivityManager.RunningTaskInfo +import android.content.Context +import android.content.res.Resources +import android.graphics.PixelFormat +import android.graphics.PointF +import android.view.LayoutInflater +import android.view.SurfaceControl +import android.view.SurfaceControl.Transaction +import android.view.SurfaceControlViewHost +import android.view.View.OnClickListener +import android.view.WindowManager +import android.view.WindowlessWindowManager +import android.widget.Button +import android.window.TaskConstants +import com.android.wm.shell.R +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.windowdecor.WindowDecoration.AdditionalWindow +import java.util.function.Supplier + + +/** + * Menu that appears when user long clicks the maximize button. Gives the user the option to + * maximize the task or snap the task to the right or left half of the screen. + */ +class MaximizeMenu( + private val syncQueue: SyncTransactionQueue, + private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer, + private val displayController: DisplayController, + private val taskInfo: RunningTaskInfo, + private val onClickListener: OnClickListener, + private val decorWindowContext: Context, + private val menuPosition: PointF, + private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() } +) { + private var maximizeMenu: AdditionalWindow? = null + private lateinit var viewHost: SurfaceControlViewHost + private lateinit var leash: SurfaceControl + private val shadowRadius = loadDimensionPixelSize( + R.dimen.desktop_mode_maximize_menu_shadow_radius + ).toFloat() + private val cornerRadius = loadDimensionPixelSize( + R.dimen.desktop_mode_maximize_menu_corner_radius + ).toFloat() + + /** Position the menu relative to the caption's position. */ + fun positionMenu(position: PointF, t: Transaction) { + menuPosition.set(position) + t.setPosition(leash, menuPosition.x, menuPosition.y) + } + + /** Creates and shows the maximize window. */ + fun show() { + if (maximizeMenu != null) return + createMaximizeMenu() + setupMaximizeMenu() + } + + /** Closes the maximize window and releases its view. */ + fun close() { + maximizeMenu?.releaseView() + maximizeMenu = null + } + + /** Create a maximize menu that is attached to the display area. */ + private fun createMaximizeMenu() { + val t = transactionSupplier.get() + val v = LayoutInflater.from(decorWindowContext).inflate( + R.layout.desktop_mode_window_decor_maximize_menu, + null // Root + ) + val builder = SurfaceControl.Builder() + rootTdaOrganizer.attachToDisplayArea(taskInfo.displayId, builder) + leash = builder + .setName("Maximize Menu") + .setContainerLayer() + .build() + val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_width) + val menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_height) + val lp = WindowManager.LayoutParams( + menuWidth, + menuHeight, + WindowManager.LayoutParams.TYPE_APPLICATION, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSPARENT + ) + lp.title = "Maximize Menu for Task=" + taskInfo.taskId + lp.setTrustedOverlay() + val windowManager = WindowlessWindowManager( + taskInfo.configuration, + leash, + null // HostInputToken + ) + viewHost = SurfaceControlViewHost(decorWindowContext, + displayController.getDisplay(taskInfo.displayId), windowManager, + "MaximizeMenu") + viewHost.setView(v, lp) + + // Bring menu to front when open + t.setLayer(leash, TaskConstants.TASK_CHILD_LAYER_FLOATING_MENU) + .setPosition(leash, menuPosition.x, menuPosition.y) + .setWindowCrop(leash, menuWidth, menuHeight) + .setShadowRadius(leash, shadowRadius) + .setCornerRadius(leash, cornerRadius) + .show(leash) + maximizeMenu = AdditionalWindow(leash, viewHost, transactionSupplier) + + syncQueue.runInSync { transaction -> + transaction.merge(t) + t.close() + } + } + + private fun loadDimensionPixelSize(resourceId: Int): Int { + return if (resourceId == Resources.ID_NULL) { + 0 + } else { + decorWindowContext.resources.getDimensionPixelSize(resourceId) + } + } + + private fun setupMaximizeMenu() { + val maximizeMenuView = maximizeMenu?.mWindowViewHost?.view ?: return + + maximizeMenuView.requireViewById<Button>( + R.id.maximize_menu_maximize_button + ).setOnClickListener(onClickListener) + maximizeMenuView.requireViewById<Button>( + R.id.maximize_menu_snap_right_button + ).setOnClickListener(onClickListener) + maximizeMenuView.requireViewById<Button>( + R.id.maximize_menu_snap_left_button + ).setOnClickListener(onClickListener) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java index bfce72bcadf0..368231e2d7f0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java @@ -49,11 +49,13 @@ public class ResizeVeil { private final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier; private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier; private final Drawable mAppIcon; + private ImageView mIconView; private SurfaceControl mParentSurface; private SurfaceControl mVeilSurface; private final RunningTaskInfo mTaskInfo; private SurfaceControlViewHost mViewHost; private final Display mDisplay; + private ValueAnimator mVeilAnimator; public ResizeVeil(Context context, Drawable appIcon, RunningTaskInfo taskInfo, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Display display, @@ -97,8 +99,8 @@ public class ResizeVeil { mViewHost = new SurfaceControlViewHost(mContext, mDisplay, windowManager, "ResizeVeil"); mViewHost.setView(v, lp); - final ImageView appIcon = mViewHost.getView().findViewById(R.id.veil_application_icon); - appIcon.setImageDrawable(mAppIcon); + mIconView = mViewHost.getView().findViewById(R.id.veil_application_icon); + mIconView.setImageDrawable(mAppIcon); } /** @@ -123,17 +125,34 @@ public class ResizeVeil { relayout(taskBounds, t); if (fadeIn) { - final ValueAnimator animator = new ValueAnimator(); - animator.setFloatValues(0f, 1f); - animator.setDuration(RESIZE_ALPHA_DURATION); - animator.addUpdateListener(animation -> { - t.setAlpha(mVeilSurface, animator.getAnimatedFraction()); + mVeilAnimator = new ValueAnimator(); + mVeilAnimator.setFloatValues(0f, 1f); + mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION); + mVeilAnimator.addUpdateListener(animation -> { + t.setAlpha(mVeilSurface, mVeilAnimator.getAnimatedFraction()); t.apply(); }); + mVeilAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + t.setAlpha(mVeilSurface, 1); + t.apply(); + } + }); + + final ValueAnimator iconAnimator = new ValueAnimator(); + iconAnimator.setFloatValues(0f, 1f); + iconAnimator.setDuration(RESIZE_ALPHA_DURATION); + iconAnimator.addUpdateListener(animation -> { + mIconView.setAlpha(animation.getAnimatedFraction()); + }); t.show(mVeilSurface) .addTransactionCommittedListener( - mContext.getMainExecutor(), () -> animator.start()) + mContext.getMainExecutor(), () -> { + mVeilAnimator.start(); + iconAnimator.start(); + }) .setAlpha(mVeilSurface, 0); } else { // Show the veil immediately at full opacity. @@ -172,11 +191,17 @@ public class ResizeVeil { /** * Calls relayout to update task and veil bounds. + * Finishes veil fade in if animation is currently running; this is to prevent empty space + * being visible behind the transparent veil during a fast resize. * * @param t a transaction to be applied in sync with the veil draw. * @param newBounds bounds to update veil to. */ public void updateResizeVeil(SurfaceControl.Transaction t, Rect newBounds) { + if (mVeilAnimator != null && mVeilAnimator.isStarted()) { + mVeilAnimator.removeAllUpdateListeners(); + mVeilAnimator.end(); + } relayout(newBounds, t); mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java index c9c58de6e82a..852c037baad5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java @@ -21,6 +21,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import android.graphics.PointF; import android.graphics.Rect; import android.os.IBinder; +import android.view.Surface; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; @@ -58,6 +59,7 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, private final int mDisallowedAreaForEndBoundsHeight; private final Supplier<SurfaceControl.Transaction> mTransactionSupplier; private int mCtrlType; + @Surface.Rotation private int mRotation; public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, DesktopModeWindowDecoration windowDecoration, DisplayController displayController, @@ -98,7 +100,10 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, } mDragStartListener.onDragStart(mDesktopWindowDecoration.mTaskInfo.taskId); mRepositionTaskBounds.set(mTaskBoundsAtDragStart); - if (mStableBounds.isEmpty()) { + int rotation = mDesktopWindowDecoration + .mTaskInfo.configuration.windowConfiguration.getDisplayRotation(); + if (mStableBounds.isEmpty() || mRotation != rotation) { + mRotation = rotation; mDisplayController.getDisplayLayout(mDesktopWindowDecoration.mDisplay.getDisplayId()) .getStableBounds(mStableBounds); } @@ -142,7 +147,9 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, // won't be called. mDesktopWindowDecoration.hideResizeVeil(); } - } else if (y > mDisallowedAreaForEndBoundsHeight) { + } else if (DragPositioningCallbackUtility.isBelowDisallowedArea( + mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint, + y)) { DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y); DragPositioningCallbackUtility.applyTaskBoundsChange(new WindowContainerTransaction(), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index 0b0d9d5086f4..d0e647b30a96 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -17,8 +17,11 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.WindowInsets.Type.statusBars; import android.app.ActivityManager.RunningTaskInfo; +import android.app.WindowConfiguration.WindowingMode; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -28,6 +31,8 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; import android.view.Display; +import android.view.InsetsSource; +import android.view.InsetsState; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; @@ -42,6 +47,7 @@ import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import java.util.function.Supplier; @@ -115,6 +121,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> SurfaceControl mCaptionContainerSurface; private WindowlessWindowManager mCaptionWindowManager; private SurfaceControlViewHost mViewHost; + private Configuration mWindowDecorConfig; + private boolean mIsCaptionVisible; private final Binder mOwner = new Binder(); private final Rect mCaptionInsetsRect = new Rect(); @@ -125,8 +133,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> DisplayController displayController, ShellTaskOrganizer taskOrganizer, RunningTaskInfo taskInfo, - SurfaceControl taskSurface) { - this(context, displayController, taskOrganizer, taskInfo, taskSurface, + SurfaceControl taskSurface, + Configuration windowDecorConfig) { + this(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, new SurfaceControlViewHostFactory() {}); } @@ -137,6 +146,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> ShellTaskOrganizer taskOrganizer, RunningTaskInfo taskInfo, SurfaceControl taskSurface, + Configuration windowDecorConfig, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, @@ -152,17 +162,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mSurfaceControlViewHostFactory = surfaceControlViewHostFactory; mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId); - mDecorWindowContext = mContext.createConfigurationContext( - getConfigurationWithOverrides(mTaskInfo)); - } - - /** - * Get {@link Configuration} from supplied {@link RunningTaskInfo}. - * - * Allows values to be overridden before returning the configuration. - */ - protected Configuration getConfigurationWithOverrides(RunningTaskInfo taskInfo) { - return taskInfo.getConfiguration(); + mWindowDecorConfig = windowDecorConfig; + mDecorWindowContext = mContext.createConfigurationContext(mWindowDecorConfig); } /** @@ -179,7 +180,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> RelayoutResult<T> outResult) { outResult.reset(); - final Configuration oldTaskConfig = mTaskInfo.getConfiguration(); if (params.mRunningTaskInfo != null) { mTaskInfo = params.mRunningTaskInfo; } @@ -198,18 +198,26 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> outResult.mRootView = rootView; rootView = null; // Clear it just in case we use it accidentally - final Configuration taskConfig = getConfigurationWithOverrides(mTaskInfo); - if (oldTaskConfig.densityDpi != taskConfig.densityDpi + + final int oldDensityDpi = mWindowDecorConfig.densityDpi; + final int oldNightMode = mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK; + mWindowDecorConfig = params.mWindowDecorConfig != null ? params.mWindowDecorConfig + : mTaskInfo.getConfiguration(); + final int newDensityDpi = mWindowDecorConfig.densityDpi; + final int newNightMode = mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK; + if (oldDensityDpi != newDensityDpi || mDisplay == null || mDisplay.getDisplayId() != mTaskInfo.displayId - || oldLayoutResId != mLayoutResId) { + || oldLayoutResId != mLayoutResId + || oldNightMode != newNightMode) { releaseViews(); if (!obtainDisplayOrRegisterListener()) { outResult.mRootView = null; return; } - mDecorWindowContext = mContext.createConfigurationContext(taskConfig); + mDecorWindowContext = mContext.createConfigurationContext(mWindowDecorConfig); + mDecorWindowContext.setTheme(mContext.getThemeResId()); if (params.mLayoutResId != 0) { outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext) .inflate(params.mLayoutResId, null); @@ -221,8 +229,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> .inflate(params.mLayoutResId, null); } + updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId); + final Resources resources = mDecorWindowContext.getResources(); + final Configuration taskConfig = mTaskInfo.getConfiguration(); final Rect taskBounds = taskConfig.windowConfiguration.getBounds(); + final boolean isFullscreen = taskConfig.windowConfiguration.getWindowingMode() + == WINDOWING_MODE_FULLSCREEN; outResult.mWidth = taskBounds.width(); outResult.mHeight = taskBounds.height(); @@ -265,33 +278,60 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> // Caption insets mCaptionInsetsRect.set(taskBounds); - mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight + params.mCaptionY; - wct.addInsetsSource(mTaskInfo.token, - mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect); - wct.addInsetsSource(mTaskInfo.token, - mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(), - mCaptionInsetsRect); + if (mIsCaptionVisible) { + mCaptionInsetsRect.bottom = + mCaptionInsetsRect.top + captionHeight + params.mCaptionY; + wct.addInsetsSource(mTaskInfo.token, + mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect); + wct.addInsetsSource(mTaskInfo.token, + mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(), + mCaptionInsetsRect); + } else { + wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */, + WindowInsets.Type.captionBar()); + wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */, + WindowInsets.Type.mandatorySystemGestures()); + } } else { startT.hide(mCaptionContainerSurface); } // Task surface itself - float shadowRadius = loadDimension(resources, params.mShadowRadiusId); - int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor(); - mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f; - mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f; - mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f; + float shadowRadius; final Point taskPosition = mTaskInfo.positionInParent; - startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight) - .setShadowRadius(mTaskSurface, shadowRadius) - .setColor(mTaskSurface, mTmpColor) + if (isFullscreen) { + // Setting the task crop to the width/height stops input events from being sent to + // some regions of the app window. See b/300324920 + // TODO(b/296921174): investigate whether crop/position needs to be set by window + // decorations at all when transition handlers are already taking ownership of the task + // surface placement/crop, especially when in fullscreen where tasks cannot be + // drag-resized by the window decoration. + startT.setWindowCrop(mTaskSurface, null); + finishT.setWindowCrop(mTaskSurface, null); + // Shadow is not needed for fullscreen tasks + shadowRadius = 0; + } else { + startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); + finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); + shadowRadius = loadDimension(resources, params.mShadowRadiusId); + } + startT.setShadowRadius(mTaskSurface, shadowRadius) .show(mTaskSurface); finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y) - .setShadowRadius(mTaskSurface, shadowRadius) - .setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); + .setShadowRadius(mTaskSurface, shadowRadius); if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { + if (!DesktopModeStatus.isVeiledResizeEnabled()) { + // When fluid resize is enabled, add a background to freeform tasks + int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor(); + mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f; + mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f; + mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f; + startT.setColor(mTaskSurface, mTmpColor); + } startT.setCornerRadius(mTaskSurface, params.mCornerRadius); finishT.setCornerRadius(mTaskSurface, params.mCornerRadius); + } else if (!DesktopModeStatus.isVeiledResizeEnabled()) { + startT.unsetColor(mTaskSurface); } if (mCaptionWindowManager == null) { @@ -325,7 +365,38 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } } - int getCaptionHeightId() { + /** + * Checks if task has entered/exited immersive mode and requires a change in caption visibility. + */ + private void updateCaptionVisibility(View rootView, int displayId) { + final InsetsState insetsState = mDisplayController.getInsetsState(displayId); + for (int i = 0; i < insetsState.sourceSize(); i++) { + final InsetsSource source = insetsState.sourceAt(i); + if (source.getType() != statusBars()) { + continue; + } + + mIsCaptionVisible = source.isVisible(); + setCaptionVisibility(rootView, mIsCaptionVisible); + + return; + } + } + + private void setCaptionVisibility(View rootView, boolean visible) { + if (rootView == null) { + return; + } + final int v = visible ? View.VISIBLE : View.GONE; + final View captionView = rootView.findViewById(getCaptionViewId()); + captionView.setVisibility(v); + } + + int getCaptionHeightId(@WindowingMode int windowingMode) { + return Resources.ID_NULL; + } + + int getCaptionViewId() { return Resources.ID_NULL; } @@ -408,13 +479,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> * @param yPos y position of new window * @param width width of new window * @param height height of new window - * @param shadowRadius radius of the shadow of the new window - * @param cornerRadius radius of the corners of the new window * @return the {@link AdditionalWindow} that was added. */ AdditionalWindow addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t, - SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height, int shadowRadius, - int cornerRadius) { + SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height) { final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); SurfaceControl windowSurfaceControl = builder .setName(namePrefix + " of Task=" + mTaskInfo.taskId) @@ -425,8 +493,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> t.setPosition(windowSurfaceControl, xPos, yPos) .setWindowCrop(windowSurfaceControl, width, height) - .setShadowRadius(windowSurfaceControl, shadowRadius) - .setCornerRadius(windowSurfaceControl, cornerRadius) .show(windowSurfaceControl); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, @@ -447,8 +513,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> * Adds caption inset source to a WCT */ public void addCaptionInset(WindowContainerTransaction wct) { - final int captionHeightId = getCaptionHeightId(); - if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL) { + final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode()); + if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL + || !mIsCaptionVisible) { return; } @@ -470,6 +537,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> int mCaptionX; int mCaptionY; + Configuration mWindowDecorConfig; + boolean mApplyStartTransactionOnDraw; void reset() { @@ -484,6 +553,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mCaptionY = 0; mApplyStartTransactionOnDraw = false; + mWindowDecorConfig = null; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt index a9eb8829bd2c..6b59ccec5148 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt @@ -5,6 +5,7 @@ import android.content.res.ColorStateList import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.view.View +import android.view.View.OnLongClickListener import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView @@ -19,6 +20,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( rootView: View, onCaptionTouchListener: View.OnTouchListener, onCaptionButtonClickListener: View.OnClickListener, + onLongClickListener: OnLongClickListener, appName: CharSequence, appIcon: Drawable ) : DesktopModeWindowDecorationViewHolder(rootView) { @@ -39,6 +41,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( openMenuButton.setOnTouchListener(onCaptionTouchListener) closeWindowButton.setOnClickListener(onCaptionButtonClickListener) maximizeWindowButton.setOnClickListener(onCaptionButtonClickListener) + maximizeWindowButton.onLongClickListener = onLongClickListener closeWindowButton.setOnTouchListener(onCaptionTouchListener) appNameTextView.text = appName appIconImageView.setImageDrawable(appIcon) diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp index b062fbd510ca..e111edc96580 100644 --- a/libs/WindowManager/Shell/tests/flicker/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/Android.bp @@ -39,23 +39,80 @@ filegroup { } filegroup { - name: "WMShellFlickerTestsPip-src", + name: "WMShellFlickerTestsPip1-src", + srcs: [ + "src/com/android/wm/shell/flicker/pip/A*.kt", + "src/com/android/wm/shell/flicker/pip/B*.kt", + "src/com/android/wm/shell/flicker/pip/C*.kt", + "src/com/android/wm/shell/flicker/pip/D*.kt", + "src/com/android/wm/shell/flicker/pip/S*.kt", + ], +} + +filegroup { + name: "WMShellFlickerTestsPip2-src", + srcs: [ + "src/com/android/wm/shell/flicker/pip/E*.kt", + ], +} + +filegroup { + name: "WMShellFlickerTestsPip3-src", srcs: ["src/com/android/wm/shell/flicker/pip/*.kt"], } filegroup { - name: "WMShellFlickerTestsSplitScreen-src", + name: "WMShellFlickerTestsPipCommon-src", + srcs: ["src/com/android/wm/shell/flicker/pip/common/*.kt"], +} + +filegroup { + name: "WMShellFlickerTestsPipApps-src", + srcs: ["src/com/android/wm/shell/flicker/pip/apps/*.kt"], +} + +filegroup { + name: "WMShellFlickerTestsSplitScreenBase-src", srcs: [ - "src/com/android/wm/shell/flicker/splitscreen/*.kt", "src/com/android/wm/shell/flicker/splitscreen/benchmark/*.kt", ], } filegroup { + name: "WMShellFlickerTestsSplitScreenGroup1-src", + srcs: [ + "src/com/android/wm/shell/flicker/splitscreen/A*.kt", + "src/com/android/wm/shell/flicker/splitscreen/B*.kt", + "src/com/android/wm/shell/flicker/splitscreen/C*.kt", + "src/com/android/wm/shell/flicker/splitscreen/D*.kt", + "src/com/android/wm/shell/flicker/splitscreen/E*.kt", + ], +} + +filegroup { + name: "WMShellFlickerTestsSplitScreenGroup2-src", + srcs: [ + "src/com/android/wm/shell/flicker/splitscreen/*.kt", + ], +} + +filegroup { + name: "WMShellFlickerServicePlatinumTests-src", + srcs: [ + "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt", + "src/com/android/wm/shell/flicker/service/*/scenarios/**/*.kt", + "src/com/android/wm/shell/flicker/service/common/**/*.kt", + ], +} + +filegroup { name: "WMShellFlickerServiceTests-src", srcs: [ "src/com/android/wm/shell/flicker/service/**/*.kt", ], + exclude_srcs: [ + "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt", + ], } java_library { @@ -77,6 +134,21 @@ java_library { "wm-flicker-common-assertions", "launcher-helper-lib", "launcher-aosp-tapl", + "com_android_wm_shell_flags_lib", + ], +} + +java_library { + name: "wm-shell-flicker-platinum-tests", + platform_apis: true, + optimize: { + enabled: false, + }, + srcs: [ + ":WMShellFlickerServicePlatinumTests-src", + ], + static_libs: [ + "wm-shell-flicker-utils", ], } @@ -97,6 +169,7 @@ java_defaults { "flickertestapplib", "flickerlib", "flickerlib-helpers", + "flickerlib-trace_processor_shell", "platform-test-annotations", "wm-flicker-common-app-helpers", "wm-flicker-common-assertions", @@ -121,9 +194,16 @@ android_test { ], exclude_srcs: [ ":WMShellFlickerTestsBubbles-src", - ":WMShellFlickerTestsPip-src", - ":WMShellFlickerTestsSplitScreen-src", + ":WMShellFlickerTestsPip1-src", + ":WMShellFlickerTestsPip2-src", + ":WMShellFlickerTestsPip3-src", + ":WMShellFlickerTestsPipCommon-src", + ":WMShellFlickerTestsPipApps-src", + ":WMShellFlickerTestsSplitScreenGroup1-src", + ":WMShellFlickerTestsSplitScreenGroup2-src", + ":WMShellFlickerTestsSplitScreenBase-src", ":WMShellFlickerServiceTests-src", + ":WMShellFlickerServicePlatinumTests-src", ], } @@ -147,19 +227,93 @@ android_test { instrumentation_target_package: "com.android.wm.shell.flicker.pip", srcs: [ ":WMShellFlickerTestsBase-src", - ":WMShellFlickerTestsPip-src", + ":WMShellFlickerTestsPip3-src", + ":WMShellFlickerTestsPipCommon-src", ], } android_test { - name: "WMShellFlickerTestsSplitScreen", + name: "WMShellFlickerTestsPip1", + defaults: ["WMShellFlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestPip.xml"], + package_name: "com.android.wm.shell.flicker.pip", + instrumentation_target_package: "com.android.wm.shell.flicker.pip", + srcs: [ + ":WMShellFlickerTestsBase-src", + ":WMShellFlickerTestsPip1-src", + ":WMShellFlickerTestsPipCommon-src", + ], +} + +android_test { + name: "WMShellFlickerTestsPip2", + defaults: ["WMShellFlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestPip.xml"], + package_name: "com.android.wm.shell.flicker.pip", + instrumentation_target_package: "com.android.wm.shell.flicker.pip", + srcs: [ + ":WMShellFlickerTestsBase-src", + ":WMShellFlickerTestsPip2-src", + ":WMShellFlickerTestsPipCommon-src", + ], +} + +android_test { + name: "WMShellFlickerTestsPip3", + defaults: ["WMShellFlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestPip.xml"], + package_name: "com.android.wm.shell.flicker.pip", + instrumentation_target_package: "com.android.wm.shell.flicker.pip", + srcs: [ + ":WMShellFlickerTestsBase-src", + ":WMShellFlickerTestsPip3-src", + ":WMShellFlickerTestsPipCommon-src", + ], + exclude_srcs: [ + ":WMShellFlickerTestsPip1-src", + ":WMShellFlickerTestsPip2-src", + ], +} + +android_test { + name: "WMShellFlickerTestsPipApps", + defaults: ["WMShellFlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestPip.xml"], + package_name: "com.android.wm.shell.flicker.pip.apps", + instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps", + srcs: [ + ":WMShellFlickerTestsBase-src", + ":WMShellFlickerTestsPipApps-src", + ":WMShellFlickerTestsPipCommon-src", + ], +} + +android_test { + name: "WMShellFlickerTestsSplitScreenGroup1", defaults: ["WMShellFlickerTestsDefault"], additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"], package_name: "com.android.wm.shell.flicker.splitscreen", instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", srcs: [ ":WMShellFlickerTestsBase-src", - ":WMShellFlickerTestsSplitScreen-src", + ":WMShellFlickerTestsSplitScreenBase-src", + ":WMShellFlickerTestsSplitScreenGroup1-src", + ], +} + +android_test { + name: "WMShellFlickerTestsSplitScreenGroup2", + defaults: ["WMShellFlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"], + package_name: "com.android.wm.shell.flicker.splitscreen", + instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", + srcs: [ + ":WMShellFlickerTestsBase-src", + ":WMShellFlickerTestsSplitScreenBase-src", + ":WMShellFlickerTestsSplitScreenGroup2-src", + ], + exclude_srcs: [ + ":WMShellFlickerTestsSplitScreenGroup1-src", ], } @@ -174,3 +328,15 @@ android_test { ":WMShellFlickerServiceTests-src", ], } + +android_test { + name: "WMShellFlickerServicePlatinumTests", + defaults: ["WMShellFlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestService.xml"], + package_name: "com.android.wm.shell.flicker.service", + instrumentation_target_package: "com.android.wm.shell.flicker.service", + srcs: [ + ":WMShellFlickerTestsBase-src", + ":WMShellFlickerServicePlatinumTests-src", + ], +} diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml index c8a96377800f..1df11369a049 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml @@ -57,6 +57,14 @@ <option name="test-file-name" value="{MODULE}.apk"/> <option name="test-file-name" value="FlickerTestApp.apk"/> </target_preparer> + <!-- Enable mocking GPS location by the test app --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/> + <option name="teardown-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/> + </target_preparer> + <!-- Needed for pushing the trace config file --> <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> @@ -90,11 +98,11 @@ <option name="directory-keys" value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker.pip/files"/> + value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker.splitscreen/files"/> + value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker.service/files"/> + value="/data/user/0/com.android.wm.shell.flicker.service/files"/> <option name="collect-on-run-ended-only" value="true"/> <option name="clean-up" value="true"/> </metrics_collector> diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml index 6a87de47def4..ae130b8f6f7d 100644 --- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml @@ -45,9 +45,13 @@ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> <!-- Enable bubble notification--> <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> - <!-- Allow the test to write directly to /sdcard/ --> - <application android:requestLegacyExternalStorage="true" android:largeHeap="true"> + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> <uses-library android:name="android.test.runner"/> <service android:name=".NotificationListener" diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml index 5a8155a66d30..fa42a4570b7d 100644 --- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml +++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml @@ -17,6 +17,9 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.wm.shell.flicker.pip"> + <!-- Enable mocking GPS location --> + <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.wm.shell.flicker.pip" android:label="WindowManager Flicker Tests"> diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml new file mode 100644 index 000000000000..4bd9ca049f55 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt index 36acb58d6139..adf92d8854ff 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt @@ -28,7 +28,6 @@ import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtStart import com.android.wm.shell.flicker.utils.appWindowKeepVisible import com.android.wm.shell.flicker.utils.layerKeepVisible - import org.junit.Assume import org.junit.Before import org.junit.Rule @@ -37,9 +36,7 @@ abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) { protected val context: Context = instrumentation.context protected val letterboxApp = LetterboxAppHelper(instrumentation) - @JvmField - @Rule - val letterboxRule: LetterboxRule = LetterboxRule() + @JvmField @Rule val letterboxRule: LetterboxRule = LetterboxRule() /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit @@ -53,7 +50,7 @@ abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) { } @Before - fun before() { + fun setUp() { Assume.assumeTrue(tapl.isTablet && letterboxRule.isIgnoreOrientationRequest) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt index 5a1136f97c6f..744e8c2eb06f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt @@ -23,16 +23,14 @@ import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement -/** - * JUnit Rule to handle letterboxStyles and states - */ +/** JUnit Rule to handle letterboxStyles and states */ class LetterboxRule( - private val withLetterboxEducationEnabled: Boolean = false, - private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(), - private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation) + private val withLetterboxEducationEnabled: Boolean = false, + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(), + private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation) ) : TestRule { - private val execAdb: (String) -> String = {cmd -> cmdHelper.executeShellCommand(cmd)} + private val execAdb: (String) -> String = { cmd -> cmdHelper.executeShellCommand(cmd) } private lateinit var _letterboxStyle: MutableMap<String, String> val letterboxStyle: Map<String, String> @@ -62,8 +60,7 @@ class LetterboxRule( var hasLetterboxEducationStateChanged = false if ("$withLetterboxEducationEnabled" != isLetterboxEducationEnabled) { hasLetterboxEducationStateChanged = true - execAdb("wm set-letterbox-style --isEducationEnabled " + - withLetterboxEducationEnabled) + execAdb("wm set-letterbox-style --isEducationEnabled " + withLetterboxEducationEnabled) } return try { object : Statement() { @@ -74,8 +71,8 @@ class LetterboxRule( } } finally { if (hasLetterboxEducationStateChanged) { - execAdb("wm set-letterbox-style --isEducationEnabled " + - isLetterboxEducationEnabled + execAdb( + "wm set-letterbox-style --isEducationEnabled " + isLetterboxEducationEnabled ) } resetLetterboxStyle() @@ -100,9 +97,10 @@ class LetterboxRule( execAdb("wm reset-letterbox-style") } - private fun asInt(str: String?): Int? = try { - str?.toInt() - } catch (e: NumberFormatException) { - null - } -}
\ No newline at end of file + private fun asInt(str: String?): Int? = + try { + str?.toInt() + } catch (e: NumberFormatException) { + null + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt index 67d5718e6c1f..1e5e42fb077e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt @@ -66,7 +66,8 @@ class OpenAppInSizeCompatModeTest(flicker: LegacyFlickerTest) : BaseAppCompat(fl */ @Postsubmit @Test - fun letterboxAppFocusedAtEnd() = flicker.assertEventLog { focusChanges(letterboxApp.`package`) } + fun letterboxAppFocusedAtEnd() = + flicker.assertEventLog { focusChanges(letterboxApp.packageName) } @Postsubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt index e6ca261a317f..2fa1ec386781 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt @@ -54,9 +54,7 @@ class OpenTransparentActivityTest(flicker: LegacyFlickerTest) : TransparentBaseA /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { - setup { - letterboxTranslucentLauncherApp.launchViaIntent(wmHelper) - } + setup { letterboxTranslucentLauncherApp.launchViaIntent(wmHelper) } transitions { waitAndGetLaunchTransparent()?.click() ?: error("Launch Transparent not found") } @@ -66,9 +64,7 @@ class OpenTransparentActivityTest(flicker: LegacyFlickerTest) : TransparentBaseA } } - /** - * Checks the transparent activity is launched on top of the opaque one - */ + /** Checks the transparent activity is launched on top of the opaque one */ @Postsubmit @Test fun translucentActivityIsLaunchedOnTopOfOpaqueActivity() { @@ -79,18 +75,14 @@ class OpenTransparentActivityTest(flicker: LegacyFlickerTest) : TransparentBaseA } } - /** - * Checks that the activity is letterboxed - */ + /** Checks that the activity is letterboxed */ @Postsubmit @Test fun translucentActivityIsLetterboxed() { flicker.assertLayers { isVisible(ComponentNameMatcher.LETTERBOX) } } - /** - * Checks that the translucent activity inherits bounds from the opaque one. - */ + /** Checks that the translucent activity inherits bounds from the opaque one. */ @Postsubmit @Test fun translucentActivityInheritsBoundsFromOpaqueActivity() { @@ -100,29 +92,26 @@ class OpenTransparentActivityTest(flicker: LegacyFlickerTest) : TransparentBaseA } } - /** - * Checks that the translucent activity has rounded corners - */ + /** Checks that the translucent activity has rounded corners */ @Postsubmit @Test fun translucentActivityHasRoundedCorners() { - flicker.assertLayersEnd { - this.hasRoundedCorners(letterboxTranslucentApp) - } + flicker.assertLayersEnd { this.hasRoundedCorners(letterboxTranslucentApp) } } companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.rotationTests] for configuring screen orientation and - * navigation modes. + * See [FlickerTestFactory.rotationTests] for configuring screen orientation and navigation + * modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return LegacyFlickerTestFactory - .nonRotationTests(supportedRotations = listOf(Rotation.ROTATION_90)) + return LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_90) + ) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt index d3f3c5b7c672..b74aa1d7bf73 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt @@ -49,8 +49,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchLauncherToLetterboxAppTest(flicker: LegacyFlickerTest) : - BaseAppCompat(flicker) { +class QuickSwitchLauncherToLetterboxAppTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit = { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt new file mode 100644 index 000000000000..ba2b3e7e2781 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.appcompat + +import android.os.Build +import android.tools.common.datatypes.Rect +import android.platform.test.annotations.Postsubmit +import android.system.helpers.CommandsHelper +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.common.flicker.assertions.FlickerTest +import android.tools.common.traces.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.traces.parsers.toFlickerComponent +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import com.android.server.wm.flicker.helpers.LetterboxAppHelper +import com.android.server.wm.flicker.testapp.ActivityOptions +import org.junit.Assume +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** + * Test rotating an immersive app in fullscreen. + * + * To run this test: `atest WMShellFlickerTestsOther:RotateImmersiveAppInFullscreenTest` + * + * Actions: + * ``` + * Rotate the device by 90 degrees to trigger a rotation through sensors + * Verify that the button exists + * ``` + * + * Notes: + * ``` + * Some default assertions that are inherited from + * the `BaseTest` are ignored due to the nature of the immersive apps. + * + * This test only works with Cuttlefish devices. + * ``` + */ +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) { + + private val immersiveApp = LetterboxAppHelper(instrumentation, + launcherName = ActivityOptions.PortraitImmersiveActivity.LABEL, + component = + ActivityOptions.PortraitImmersiveActivity.COMPONENT.toFlickerComponent()) + + private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation) + private val execAdb: (String) -> String = { cmd -> cmdHelper.executeShellCommand(cmd) } + + protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) + + private val isCuttlefishDevice: Boolean = Build.MODEL.contains("Cuttlefish") + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit + get() = { + setup { + setStartRotation() + immersiveApp.launchViaIntent(wmHelper) + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds + ?: error("Display not found") + } + transitions { + if (isCuttlefishDevice) { + // Simulates a device rotation through sensors because the rotation button + // only appears in a rotation event through sensors + execAdb("/vendor/bin/cuttlefish_sensor_injection rotate 0") + // verify rotation button existence + val rotationButtonSelector = By.res(LAUNCHER_PACKAGE, "rotate_suggestion") + uiDevice.wait(Until.hasObject(rotationButtonSelector), FIND_TIMEOUT) + uiDevice.findObject(rotationButtonSelector) + ?: error("rotation button not found") + } + } + teardown { + immersiveApp.exit(wmHelper) + } + } + + @Before + fun setUpForImmersiveAppTests() { + Assume.assumeTrue(isCuttlefishDevice) + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun taskBarLayerIsVisibleAtStartAndEnd() { + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun navBarLayerIsVisibleAtStartAndEnd() { + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun statusBarLayerIsVisibleAtStartAndEnd() { + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun taskBarWindowIsAlwaysVisible() { + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun navBarWindowIsAlwaysVisible() { + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun statusBarWindowIsAlwaysVisible() { + } + + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun statusBarLayerPositionAtStartAndEnd() { + } + + @Test + @Ignore("Not applicable to this CUJ. App is in immersive mode.") + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + } + + /** Test that app is fullscreen by checking status bar and task bar visibility. */ + @Postsubmit + @Test + fun appWindowFullScreen() { + flicker.assertWmEnd { + this.isAppWindowInvisible(ComponentNameMatcher.STATUS_BAR) + .isAppWindowInvisible(ComponentNameMatcher.TASK_BAR) + .visibleRegion(immersiveApp).coversExactly(startDisplayBounds) + } + } + + /** Test that app is in the original rotation we have set up. */ + @Postsubmit + @Test + fun appInOriginalRotation() { + flicker.assertWmEnd { + this.hasRotation(Rotation.ROTATION_90) + } + } + + companion object { + private var startDisplayBounds = Rect.EMPTY + const val LAUNCHER_PACKAGE = "com.google.android.apps.nexuslauncher" + + /** + * Creates the test configurations. + * + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + return LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_90), + // TODO(b/292403378): 3 button mode not added as rotation button is hidden in taskbar + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt index ea0392cee95a..9792c859cced 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt @@ -33,20 +33,20 @@ import org.junit.Rule abstract class TransparentBaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) { protected val context: Context = instrumentation.context - protected val letterboxTranslucentLauncherApp = LetterboxAppHelper( - instrumentation, - launcherName = ActivityOptions.LaunchTransparentActivity.LABEL, - component = ActivityOptions.LaunchTransparentActivity.COMPONENT.toFlickerComponent() - ) - protected val letterboxTranslucentApp = LetterboxAppHelper( - instrumentation, - launcherName = ActivityOptions.TransparentActivity.LABEL, - component = ActivityOptions.TransparentActivity.COMPONENT.toFlickerComponent() - ) + protected val letterboxTranslucentLauncherApp = + LetterboxAppHelper( + instrumentation, + launcherName = ActivityOptions.LaunchTransparentActivity.LABEL, + component = ActivityOptions.LaunchTransparentActivity.COMPONENT.toFlickerComponent() + ) + protected val letterboxTranslucentApp = + LetterboxAppHelper( + instrumentation, + launcherName = ActivityOptions.TransparentActivity.LABEL, + component = ActivityOptions.TransparentActivity.COMPONENT.toFlickerComponent() + ) - @JvmField - @Rule - val letterboxRule: LetterboxRule = LetterboxRule() + @JvmField @Rule val letterboxRule: LetterboxRule = LetterboxRule() @Before fun before() { @@ -54,10 +54,7 @@ abstract class TransparentBaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(f } protected fun FlickerTestData.waitAndGetLaunchTransparent(): UiObject2? = - device.wait( - Until.findObject(By.text("Launch Transparent")), - FIND_TIMEOUT - ) + device.wait(Until.findObject(By.text("Launch Transparent")), FIND_TIMEOUT) protected fun FlickerTestData.goBack() = device.pressBack() } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt index 5c7d1d8df2e8..bc095bbacc5a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt @@ -31,6 +31,7 @@ import androidx.test.uiautomator.By import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until import com.android.server.wm.flicker.helpers.LaunchBubbleHelper +import com.android.server.wm.flicker.helpers.MultiWindowUtils import com.android.wm.shell.flicker.BaseTest import org.junit.runners.Parameterized @@ -47,7 +48,7 @@ abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker) private val uid = context.packageManager - .getApplicationInfo(testApp.`package`, PackageManager.ApplicationInfoFlags.of(0)) + .getApplicationInfo(testApp.packageName, PackageManager.ApplicationInfoFlags.of(0)) .uid @JvmOverloads @@ -56,8 +57,11 @@ abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker) ): FlickerBuilder.() -> Unit { return { setup { + MultiWindowUtils.executeShellCommand( + instrumentation, + "settings put secure force_hide_bubbles_user_education 1") notifyManager.setBubblesAllowed( - testApp.`package`, + testApp.packageName, uid, NotificationManager.BUBBLE_PREFERENCE_ALL ) @@ -67,8 +71,11 @@ abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker) } teardown { + MultiWindowUtils.executeShellCommand( + instrumentation, + "settings put secure force_hide_bubbles_user_education 0") notifyManager.setBubblesAllowed( - testApp.`package`, + testApp.packageName, uid, NotificationManager.BUBBLE_PREFERENCE_NONE ) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt index bc565bc5fd42..55039f59190b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt @@ -17,12 +17,11 @@ package com.android.wm.shell.flicker.bubble import android.os.SystemClock -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice +import androidx.test.filters.FlakyTest import androidx.test.uiautomator.By import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until @@ -40,12 +39,10 @@ import org.junit.runners.Parameterized * Switch in different bubble notifications * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FlakyTest(bugId = 217777115) -open class ChangeActiveActivityFromBubbleTest(flicker: LegacyFlickerTest) : - BaseBubbleScreen(flicker) { +class ChangeActiveActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = buildTransition { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt index 3f28ae848d1f..9ca7bf113589 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt @@ -20,12 +20,12 @@ import android.content.Context import android.graphics.Point import android.platform.test.annotations.Presubmit import android.tools.common.flicker.subject.layers.LayersTraceSubject +import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.util.DisplayMetrics import android.view.WindowManager -import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import org.junit.Test @@ -42,10 +42,9 @@ import org.junit.runners.Parameterized * Dismiss a bubble notification * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { +class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { private val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager private val displaySize = DisplayMetrics() @@ -80,7 +79,8 @@ open class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbl override fun visibleLayersShownMoreThanOneConsecutiveEntry() { flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry( - LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(testApp) + LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + + listOf(testApp, ComponentNameMatcher(className = "Bubbles!#")) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt index 26aca1830889..b007e6b3535c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.flicker.bubble -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory @@ -24,6 +23,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.view.WindowInsets import android.view.WindowManager +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt index 508539411aa0..4959672d865b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt @@ -20,7 +20,6 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import org.junit.Test @@ -39,10 +38,9 @@ import org.junit.runners.Parameterized * The activity for the bubble is launched * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class OpenActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { +class OpenActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt index a926bb7d85c3..0d95574aca06 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt @@ -20,7 +20,6 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import org.junit.Test @@ -38,10 +37,9 @@ import org.junit.runners.Parameterized * Send a bubble notification * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class SendBubbleNotificationTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { +class SendBubbleNotificationTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt index c335d3dc7f4b..943b16c33a98 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt @@ -80,12 +80,12 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) : secondAppForSplitScreen.launchViaIntent(wmHelper) pipApp.launchViaIntent(wmHelper) tapl.goHome() - SplitScreenUtils.enterSplit(wmHelper, tapl, device, pipApp, secondAppForSplitScreen) + SplitScreenUtils.enterSplit( + wmHelper, tapl, device, pipApp, secondAppForSplitScreen, + flicker.scenario.startRotation) pipApp.enableAutoEnterForPipActivity() } teardown { - // close gracefully so that onActivityUnpinned() can be called before force exit - pipApp.closePipWindow(wmHelper) pipApp.exit(wmHelper) secondAppForSplitScreen.exit(wmHelper) } @@ -126,9 +126,20 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) : if (tapl.isTablet) { flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) } } else { - // on phones home does not rotate in landscape, PiP enters back to portrait - // orientation so use display bounds from that orientation for assertion - flicker.assertWmVisibleRegion(pipApp) { coversAtMost(portraitDisplayBounds) } + // on phones home screen does not rotate in landscape, PiP enters back to portrait + // orientation - if we go from landscape to portrait it should switch between the bounds + // otherwise it should be the same as tablet (i.e. portrait to portrait) + if (flicker.scenario.isLandscapeOrSeascapeAtStart) { + flicker.assertWmVisibleRegion(pipApp) { + // first check against landscape bounds then against portrait bounds + coversAtMost(displayBounds).then().coversAtMost( + portraitDisplayBounds + ) + } + } else { + // always check against the display bounds which do not change during transition + flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) } + } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt index 2f7a25ea586d..94e3959782ed 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt @@ -20,7 +20,9 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.EnterPipTransition import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -54,7 +56,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : - EnterPipViaAppUiButtonTest(flicker) { + EnterPipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } } override val defaultEnterPip: FlickerBuilder.() -> Unit = { @@ -66,13 +68,11 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { - // close gracefully so that onActivityUnpinned() can be called before force exit - pipApp.closePipWindow(wmHelper) pipApp.exit(wmHelper) } } - @Presubmit + @FlakyTest(bugId = 293133362) @Test override fun pipLayerReduces() { flicker.assertLayers { @@ -84,7 +84,7 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : } /** Checks that [pipApp] window is animated towards default position in right bottom corner */ - @Presubmit + @FlakyTest(bugId = 255578530) @Test fun pipLayerMovesTowardsRightBottomCorner() { // in gestural nav the swipe makes PiP first go upwards @@ -107,4 +107,10 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : Assume.assumeFalse(flicker.scenario.isGesturalNavigation) super.focusChanges() } + + @FlakyTest(bugId = 289943985) + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { + super.visibleLayersShownMoreThanOneConsecutiveEntry() + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt index 68bc9a28967e..9256725c6180 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt @@ -21,7 +21,7 @@ import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.ClosePipTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -49,11 +49,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ClosePipBySwipingDownTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) { +class ClosePipBySwipingDownTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { val pipRegion = wmHelper.getWindowRegion(pipApp).bounds diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt deleted file mode 100644 index 7a668897fbbe..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ClosePipBySwipingDownTestCfArm(flicker: LegacyFlickerTest) : - ClosePipBySwipingDownTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen - * orientation and navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt index dc48696f3197..002c019eff93 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt @@ -20,7 +20,7 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.ClosePipTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -49,11 +49,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ClosePipWithDismissButtonTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) { +class ClosePipWithDismissButtonTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { pipApp.closePipWindow(wmHelper) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt deleted file mode 100644 index 718b14babc4f..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ClosePipWithDismissButtonTestCfArm(flicker: LegacyFlickerTest) : - ClosePipWithDismissButtonTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen - * orientation and navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt index 5e392628aa6a..820af9316aae 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt @@ -20,7 +20,7 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.EnterPipTransition import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -40,11 +40,10 @@ import org.junit.runners.Parameterized * Press Home button or swipe up to go Home and put [pipApp] in pip mode * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) { +class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } } override val defaultEnterPip: FlickerBuilder.() -> Unit = { @@ -56,8 +55,6 @@ open class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTra override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { - // close gracefully so that onActivityUnpinned() can be called before force exit - pipApp.closePipWindow(wmHelper) pipApp.exit(wmHelper) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt deleted file mode 100644 index 2b3e76a964c4..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** This test will fail because of b/264261596 */ -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterPipOnUserLeaveHintTestCfArm(flicker: LegacyFlickerTest) : - EnterPipOnUserLeaveHintTest(flicker) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt index ec35837bc8dd..8207b85c3e0c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.pip import android.app.Activity -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.Rotation @@ -28,13 +27,14 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils -import androidx.test.filters.RequiresDevice +import androidx.test.filters.FlakyTest import com.android.server.wm.flicker.entireScreenCovered import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION -import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE -import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT +import com.android.wm.shell.flicker.pip.common.PipTransition +import com.android.wm.shell.flicker.pip.common.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE +import com.android.wm.shell.flicker.pip.common.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT import org.junit.Assume import org.junit.Before import org.junit.FixMethodOrder @@ -65,11 +65,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) { +class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) { private val testApp = FixedOrientationAppHelper(instrumentation) private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90) private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt deleted file mode 100644 index 92642197e9be..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.common.flicker.assertions.FlickerTest -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** This test fails because of b/264261596 */ -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterPipToOtherOrientationCfArm(flicker: LegacyFlickerTest) : - EnterPipToOtherOrientation(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTest> { - return LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt index 76c811cbbeea..cc943678d492 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt @@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.EnterPipTransition import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -46,7 +46,6 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt deleted file mode 100644 index 78e80497747c..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterPipViaAppUiButtonTestCfArm(flicker: LegacyFlickerTest) : - EnterPipViaAppUiButtonTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen - * orientation and navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt index b80b7483ba4d..7da442901e40 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt @@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.ExitPipToAppTransition import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -48,11 +48,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) : +class ExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { setup { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt index f003ed8a77e0..0ad9e4c61d83 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt @@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.ExitPipToAppTransition import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -47,11 +47,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) { +class ExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { setup { // launch an app behind the pip one diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt deleted file mode 100644 index be19f3cd1970..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExitPipToAppViaIntentTestCfArm(flicker: LegacyFlickerTest) : - ExitPipToAppViaIntentTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen - * orientation and navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt index a1d3a117482e..89a6c93e478c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt @@ -23,7 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.PipTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -51,11 +51,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExpandPipOnDoubleClickTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { +class ExpandPipOnDoubleClickTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { pipApp.doubleClickPipWindow(wmHelper) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt deleted file mode 100644 index 3095cac94598..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExpandPipOnDoubleClickTestTestCfArm(flicker: LegacyFlickerTest) : - ExpandPipOnDoubleClickTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt index 8c8d280aea9a..8978af0088b8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt @@ -22,7 +22,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.PipTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -30,11 +30,10 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** Test expanding a pip window via pinch out gesture. */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExpandPipOnPinchOpenTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { +class ExpandPipOnPinchOpenTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.25f, 30) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt deleted file mode 100644 index 1a1ce6823f3b..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExpandPipOnPinchOpenTestCfArm(flicker: LegacyFlickerTest) : - ExpandPipOnPinchOpenTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt index 421ad757f76a..4776206724cc 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt @@ -21,6 +21,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.MovePipShelfHeightTransition import com.android.wm.shell.flicker.utils.Direction import org.junit.FixMethodOrder import org.junit.Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt index dffc822e7aec..425cbfaffedd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt @@ -25,9 +25,9 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ImeAppHelper import com.android.server.wm.flicker.helpers.setRotation +import com.android.wm.shell.flicker.pip.common.PipTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -35,11 +35,10 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** Test Pip launch. To run this test: `atest WMShellFlickerTests:PipKeyboardTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class MovePipOnImeVisibilityChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { +class MovePipOnImeVisibilityChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { private val imeApp = ImeAppHelper(instrumentation) override val thisTransition: FlickerBuilder.() -> Unit = { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt deleted file mode 100644 index 63292a4f2ca3..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.common.flicker.assertions.FlickerTest -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class MovePipOnImeVisibilityChangeTestCfArm(flicker: LegacyFlickerTest) : - MovePipOnImeVisibilityChangeTest(flicker) { - companion object { - private const val TAG_IME_VISIBLE = "imeIsVisible" - - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTest> { - return LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt index 992f1bc4ace3..18f30d96938b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt @@ -21,6 +21,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.MovePipShelfHeightTransition import com.android.wm.shell.flicker.utils.Direction import org.junit.FixMethodOrder import org.junit.Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt index e25c0d6eddc0..4f27ceddd705 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt @@ -16,20 +16,37 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import com.android.wm.shell.flicker.pip.common.PipTransition import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized +/** Test changing aspect ratio of pip. */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExitPipToAppViaExpandButtonTestCfArm(flicker: LegacyFlickerTest) : - ExitPipToAppViaExpandButtonTest(flicker) { +class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { + pipApp.changeAspectRatio() + } + } + + @Presubmit + @Test + fun pipAspectRatioChangesProperly() { + flicker.assertLayersStart { this.visibleRegion(pipApp).isSameAspectRatio(16, 9) } + flicker.assertLayersEnd { this.visibleRegion(pipApp).isSameAspectRatio(1, 2) } + } + companion object { /** * Creates the test configurations. diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt index 0c6fc5636a5b..c7f2786debd0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt @@ -23,6 +23,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.wm.shell.flicker.pip.common.PipTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt index de64f78a31eb..cabc1cc0b023 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt @@ -17,16 +17,17 @@ package com.android.wm.shell.flicker.pip import android.graphics.Rect -import android.platform.test.annotations.Presubmit import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.wm.shell.flicker.pip.common.PipTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -34,6 +35,7 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** Test the snapping of a PIP window via dragging, releasing, and checking its final location. */ +@FlakyTest(bugId = 294993100) @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @@ -80,8 +82,8 @@ class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { /** * Checks that the visible region area of [pipApp] moves to closest edge during the animation. */ - @Presubmit @Test + @FlakyTest(bugId = 294993100) fun pipLayerMovesToClosestEdge() { flicker.assertLayers { val pipLayerList = layers { pipApp.layerMatchesAnyOf(it) && it.isVisible } @@ -95,6 +97,90 @@ class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { } } + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun entireScreenCovered() { + super.entireScreenCovered() + } + + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun hasAtMostOnePipDismissOverlayWindow() { + super.hasAtMostOnePipDismissOverlayWindow() + } + + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun navBarLayerIsVisibleAtStartAndEnd() { + super.navBarLayerIsVisibleAtStartAndEnd() + } + + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun navBarLayerPositionAtStartAndEnd() { + super.navBarLayerPositionAtStartAndEnd() + } + + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun navBarWindowIsAlwaysVisible() { + super.navBarWindowIsAlwaysVisible() + } + + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun statusBarLayerIsVisibleAtStartAndEnd() { + super.statusBarLayerIsVisibleAtStartAndEnd() + } + + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun statusBarLayerPositionAtStartAndEnd() { + super.statusBarLayerPositionAtStartAndEnd() + } + + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun statusBarWindowIsAlwaysVisible() { + super.statusBarWindowIsAlwaysVisible() + } + + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { + super.visibleLayersShownMoreThanOneConsecutiveEntry() + } + + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + super.visibleWindowsShownMoreThanOneConsecutiveEntry() + } + + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun taskBarLayerIsVisibleAtStartAndEnd() { + super.taskBarLayerIsVisibleAtStartAndEnd() + } + + // Overridden to remove @Presubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun taskBarWindowIsAlwaysVisible() { + super.taskBarWindowIsAlwaysVisible() + } + companion object { /** * Creates the test configurations. diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt index 0295741bc441..0fd1b2c3f0de 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt @@ -18,11 +18,13 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.tools.common.Rotation +import android.tools.common.flicker.subject.exceptions.IncorrectRegionException import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.pip.common.PipTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -39,14 +41,26 @@ class PipPinchInTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) } } - /** Checks that the visible region area of [pipApp] always decreases during the animation. */ + /** + * Checks that the visible region area of [pipApp] decreases + * and then increases during the animation. + */ @Presubmit @Test - fun pipLayerAreaDecreases() { + fun pipLayerAreaDecreasesThenIncreases() { + val isAreaDecreasing = arrayOf(true) flicker.assertLayers { val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible } pipLayerList.zipWithNext { previous, current -> - current.visibleRegion.notBiggerThan(previous.visibleRegion.region) + if (isAreaDecreasing[0]) { + try { + current.visibleRegion.notBiggerThan(previous.visibleRegion.region) + } catch (e: IncorrectRegionException) { + isAreaDecreasing[0] = false + } + } else { + previous.visibleRegion.notBiggerThan(current.visibleRegion.region) + } } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt index c315e744bd55..1f69847e5481 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.pip import android.app.Activity -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.Rotation @@ -27,10 +26,12 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION -import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE +import com.android.wm.shell.flicker.pip.common.PipTransition +import com.android.wm.shell.flicker.pip.common.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE import org.junit.Assume import org.junit.Before import org.junit.FixMethodOrder diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt index 0ff9cfff873e..308ece40402f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt @@ -23,9 +23,9 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.server.wm.flicker.helpers.setRotation +import com.android.wm.shell.flicker.pip.common.PipTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -55,11 +55,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowPipAndRotateDisplay(flicker: LegacyFlickerTest) : PipTransition(flicker) { +class ShowPipAndRotateDisplay(flicker: LegacyFlickerTest) : PipTransition(flicker) { private val testApp = SimpleAppHelper(instrumentation) private val screenBoundsStart = WindowUtils.getDisplayBounds(flicker.scenario.startRotation) private val screenBoundsEnd = WindowUtils.getDisplayBounds(flicker.scenario.endRotation) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt deleted file mode 100644 index 25164711b2e5..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.flicker.pip - -import android.tools.common.flicker.assertions.FlickerTest -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowPipAndRotateDisplayCfArm(flicker: LegacyFlickerTest) : ShowPipAndRotateDisplay(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen - * orientation and navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTest> { - return LegacyFlickerTestFactory.rotationTests() - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt new file mode 100644 index 000000000000..c9a98c73e5e5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip.apps + +import android.platform.test.annotations.Postsubmit +import android.tools.common.Rotation +import android.tools.common.traces.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import com.android.wm.shell.flicker.pip.common.EnterPipTransition +import org.junit.Test +import org.junit.runners.Parameterized + +abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) { + protected abstract val standardAppHelper: StandardAppHelper + + /** Checks [standardAppHelper] window remains visible throughout the animation */ + @Postsubmit + @Test + override fun pipAppWindowAlwaysVisible() { + flicker.assertWm { this.isAppWindowVisible(standardAppHelper.packageNameMatcher) } + } + + /** Checks [standardAppHelper] layer remains visible throughout the animation */ + @Postsubmit + @Test + override fun pipAppLayerAlwaysVisible() { + flicker.assertLayers { this.isVisible(standardAppHelper.packageNameMatcher) } + } + + /** Checks the content overlay appears then disappears during the animation */ + @Postsubmit + @Test + override fun pipOverlayLayerAppearThenDisappear() { + super.pipOverlayLayerAppearThenDisappear() + } + + /** + * Checks that [standardAppHelper] window remains inside the display bounds throughout the whole + * animation + */ + @Postsubmit + @Test + override fun pipWindowRemainInsideVisibleBounds() { + flicker.assertWmVisibleRegion(standardAppHelper.packageNameMatcher) { + coversAtMost(displayBounds) + } + } + + /** + * Checks that the [standardAppHelper] layer remains inside the display bounds throughout the + * whole animation + */ + @Postsubmit + @Test + override fun pipLayerOrOverlayRemainInsideVisibleBounds() { + flicker.assertLayersVisibleRegion( + standardAppHelper.packageNameMatcher.or(ComponentNameMatcher.PIP_CONTENT_OVERLAY) + ) { + coversAtMost(displayBounds) + } + } + + /** Checks that the visible region of [standardAppHelper] always reduces during the animation */ + @Postsubmit + @Test + override fun pipLayerReduces() { + flicker.assertLayers { + val pipLayerList = this.layers { + standardAppHelper.layerMatchesAnyOf(it) && it.isVisible + } + pipLayerList.zipWithNext { previous, current -> + current.visibleRegion.notBiggerThan(previous.visibleRegion.region) + } + } + } + + /** Checks that [standardAppHelper] window becomes pinned */ + @Postsubmit + @Test + override fun pipWindowBecomesPinned() { + flicker.assertWm { + invoke("pipWindowIsNotPinned") { it.isNotPinned(standardAppHelper.packageNameMatcher) } + .then() + .invoke("pipWindowIsPinned") { it.isPinned(standardAppHelper.packageNameMatcher) } + } + } + + /** Checks [ComponentNameMatcher.LAUNCHER] layer remains visible throughout the animation */ + @Postsubmit + @Test + override fun launcherLayerBecomesVisible() { + super.launcherLayerBecomesVisible() + } + + /** + * Checks that the focus changes between the [standardAppHelper] window and the launcher when + * closing the pip window + */ + @Postsubmit + @Test + override fun focusChanges() { + flicker.assertEventLog { + this.focusChanges(standardAppHelper.packageName, "NexusLauncherActivity") + } + } + + @Postsubmit + @Test + override fun hasAtMostOnePipDismissOverlayWindow() = super.hasAtMostOnePipDismissOverlayWindow() + + // ICommonAssertions.kt overrides due to Morris overlay + + /** + * Checks that the [ComponentNameMatcher.NAV_BAR] layer is visible during the whole transition + */ + @Postsubmit + @Test + override fun navBarLayerIsVisibleAtStartAndEnd() { + // this fails due to Morris overlay + } + + /** + * Checks the position of the [ComponentNameMatcher.NAV_BAR] at the start and end of the + * transition + */ + @Postsubmit + @Test + override fun navBarLayerPositionAtStartAndEnd() { + // this fails due to Morris overlay + } + + /** + * Checks that the [ComponentNameMatcher.NAV_BAR] window is visible during the whole transition + * + * Note: Phones only + */ + @Postsubmit + @Test + override fun navBarWindowIsAlwaysVisible() { + // this fails due to Morris overlay + } + + /** + * Checks that the [ComponentNameMatcher.TASK_BAR] layer is visible during the whole transition + */ + @Postsubmit + @Test + override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() + + /** + * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition + * + * Note: Large screen only + */ + @Postsubmit + @Test + override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() + + /** + * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible during the whole + * transition + */ + @Postsubmit + @Test + override fun statusBarLayerIsVisibleAtStartAndEnd() = + super.statusBarLayerIsVisibleAtStartAndEnd() + + /** + * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the + * transition + */ + @Postsubmit + @Test + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() + + /** + * Checks that the [ComponentNameMatcher.STATUS_BAR] window is visible during the whole + * transition + */ + @Postsubmit + @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() + + /** + * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive + * entries. + */ + @Postsubmit + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() + + /** + * Checks that all windows that are visible on the trace, are visible for at least 2 consecutive + * entries. + */ + @Postsubmit + @Test + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() + + /** Checks that all parts of the screen are covered during the transition */ + @Postsubmit + @Test + override fun entireScreenCovered() = super.entireScreenCovered() + + companion object { + /** + * Creates the test configurations. + * + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen + * orientation and navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_0) + ) + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt new file mode 100644 index 000000000000..d7ba3d57b548 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip.apps + +import android.content.Context +import android.location.Criteria +import android.location.Location +import android.location.LocationManager +import android.os.Handler +import android.os.Looper +import android.os.SystemClock +import android.platform.test.annotations.Postsubmit +import android.tools.device.apphelpers.MapsAppHelper +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.LegacyFlickerTest +import androidx.test.filters.RequiresDevice +import org.junit.Assume +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test entering pip from Maps app by interacting with the app UI + * + * To run this test: `atest WMShellFlickerTests:MapsEnterPipTest` + * + * Actions: + * ``` + * Launch Maps and start navigation mode + * Go home to enter PiP + * ``` + * + * Notes: + * ``` + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited from [PipTransition] + * 2. Part of the test setup occurs automatically via + * [android.tools.device.flicker.legacy.runner.TransitionRunner], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + * ``` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class MapsEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) { + override val standardAppHelper: MapsAppHelper = MapsAppHelper(instrumentation) + + val locationManager: LocationManager = + instrumentation.context.getSystemService(Context.LOCATION_SERVICE) as LocationManager + val mainHandler = Handler(Looper.getMainLooper()) + var mockLocationEnabled = false + + val updateLocation = object : Runnable { + override fun run() { + // early bail out if mocking location is not enabled + if (!mockLocationEnabled) return + val location = Location("Googleplex") + location.latitude = 37.42243438411294 + location.longitude = -122.08426281892311 + location.time = System.currentTimeMillis() + location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos() + location.accuracy = 100f + locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, location) + mainHandler.postDelayed(this, 5) + } + } + + override val defaultEnterPip: FlickerBuilder.() -> Unit = { + setup { + locationManager.addTestProvider( + LocationManager.GPS_PROVIDER, + false, + false, + false, + false, + false, + false, + false, + Criteria.POWER_LOW, + Criteria.ACCURACY_FINE + ) + locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true) + mockLocationEnabled = true + mainHandler.post(updateLocation) + + // normal app open through the Launcher All Apps + // var mapsAddressOption = "Golden Gate Bridge" + // standardAppHelper.open() + // standardAppHelper.doSearch(mapsAddressOption) + // standardAppHelper.getDirections() + // standardAppHelper.startNavigation(); + + standardAppHelper.launchViaIntent( + wmHelper, + MapsAppHelper.getMapIntent(MapsAppHelper.INTENT_NAVIGATION) + ) + + standardAppHelper.waitForNavigationToStart() + } + } + + override val defaultTeardown: FlickerBuilder.() -> Unit = { + teardown { + standardAppHelper.exit(wmHelper) + mainHandler.removeCallbacks(updateLocation) + // the main looper callback might have tried to provide a new location after the + // provider is no longer in test mode, causing a crash, this prevents it from happening + mockLocationEnabled = false + locationManager.removeTestProvider(LocationManager.GPS_PROVIDER) + } + } + + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { tapl.goHome() } + } + + @Postsubmit + @Test + override fun focusChanges() { + // in gestural nav the focus goes to different activity on swipe up with auto enter PiP + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.focusChanges() + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt new file mode 100644 index 000000000000..2cd08a4a58a6 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip.apps + +import android.platform.test.annotations.Postsubmit +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.common.traces.component.ComponentNameMatcher +import android.tools.device.apphelpers.NetflixAppHelper +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.RequiresDevice +import org.junit.Assume +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test entering pip from Netflix app by interacting with the app UI + * + * To run this test: `atest WMShellFlickerTests:NetflixEnterPipTest` + * + * Actions: + * ``` + * Launch Netflix and start playing a video + * Go home to enter PiP + * ``` + * + * Notes: + * ``` + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited from [PipTransition] + * 2. Part of the test setup occurs automatically via + * [android.tools.device.flicker.legacy.runner.TransitionRunner], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + * ``` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class NetflixEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) { + override val standardAppHelper: NetflixAppHelper = NetflixAppHelper(instrumentation) + + override val defaultEnterPip: FlickerBuilder.() -> Unit = { + setup { + standardAppHelper.launchViaIntent( + wmHelper, + NetflixAppHelper.getNetflixWatchVideoIntent("70184207"), + ComponentNameMatcher(NetflixAppHelper.PACKAGE_NAME, + NetflixAppHelper.WATCH_ACTIVITY) + ) + standardAppHelper.waitForVideoPlaying() + } + } + + override val defaultTeardown: FlickerBuilder.() -> Unit = { + teardown { + standardAppHelper.exit(wmHelper) + } + } + + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { + tapl.goHomeFromImmersiveFullscreenApp() + } + } + + @Postsubmit + @Test + override fun pipOverlayLayerAppearThenDisappear() { + // Netflix uses source rect hint, so PiP overlay is never present + } + + @Postsubmit + @Test + override fun focusChanges() { + // in gestural nav the focus goes to different activity on swipe up with auto enter PiP + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.focusChanges() + } + + companion object { + /** + * Creates the test configurations. + * + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen + * orientation and navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_0), + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt new file mode 100644 index 000000000000..c370d91034fd --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip.apps + +import android.platform.test.annotations.Postsubmit +import android.tools.common.traces.component.ComponentNameMatcher +import android.tools.device.apphelpers.YouTubeAppHelper +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.LegacyFlickerTest +import androidx.test.filters.RequiresDevice +import org.junit.Assume +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test entering pip from YouTube app by interacting with the app UI + * + * To run this test: `atest WMShellFlickerTests:YouTubeEnterPipTest` + * + * Actions: + * ``` + * Launch YouTube and start playing a video + * Go home to enter PiP + * ``` + * + * Notes: + * ``` + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited from [PipTransition] + * 2. Part of the test setup occurs automatically via + * [android.tools.device.flicker.legacy.runner.TransitionRunner], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + * ``` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class YouTubeEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) { + override val standardAppHelper: YouTubeAppHelper = YouTubeAppHelper(instrumentation) + + override val defaultEnterPip: FlickerBuilder.() -> Unit = { + setup { + standardAppHelper.launchViaIntent( + wmHelper, + YouTubeAppHelper.getYoutubeVideoIntent("HPcEAtoXXLA"), + ComponentNameMatcher(YouTubeAppHelper.PACKAGE_NAME, "") + ) + standardAppHelper.waitForVideoPlaying() + } + } + + override val defaultTeardown: FlickerBuilder.() -> Unit = { + teardown { + standardAppHelper.exit(wmHelper) + } + } + + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { tapl.goHome() } + } + + @Postsubmit + @Test + override fun pipOverlayLayerAppearThenDisappear() { + // YouTube uses source rect hint, so PiP overlay is never present + } + + @Postsubmit + @Test + override fun focusChanges() { + // in gestural nav the focus goes to different activity on swipe up with auto enter PiP + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.focusChanges() + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt index a17144b7cef3..751f2bc0807f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.pip +package com.android.wm.shell.flicker.pip.common import android.platform.test.annotations.Presubmit import android.tools.common.Rotation diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt index 6d20740e239c..9c129e47efba 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.pip +package com.android.wm.shell.flicker.pip.common import android.platform.test.annotations.Presubmit import android.tools.common.Rotation @@ -91,7 +91,7 @@ abstract class EnterPipTransition(flicker: LegacyFlickerTest) : PipTransition(fl /** Checks that [pipApp] window becomes pinned */ @Presubmit @Test - fun pipWindowBecomesPinned() { + open fun pipWindowBecomesPinned() { flicker.assertWm { invoke("pipWindowIsNotPinned") { it.isNotPinned(pipApp) } .then() @@ -102,7 +102,7 @@ abstract class EnterPipTransition(flicker: LegacyFlickerTest) : PipTransition(fl /** Checks [ComponentNameMatcher.LAUNCHER] layer remains visible throughout the animation */ @Presubmit @Test - fun launcherLayerBecomesVisible() { + open fun launcherLayerBecomesVisible() { flicker.assertLayers { isInvisible(ComponentNameMatcher.LAUNCHER) .then() @@ -117,7 +117,7 @@ abstract class EnterPipTransition(flicker: LegacyFlickerTest) : PipTransition(fl @Presubmit @Test open fun focusChanges() { - flicker.assertEventLog { this.focusChanges(pipApp.`package`, "NexusLauncherActivity") } + flicker.assertEventLog { this.focusChanges(pipApp.packageName, "NexusLauncherActivity") } } companion object { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt index dfffba831dc3..9450bdd2d894 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.pip +package com.android.wm.shell.flicker.pip.common import android.platform.test.annotations.Presubmit import android.tools.common.Rotation diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt index a8fb63de244b..7e42bc11a9c1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.pip +package com.android.wm.shell.flicker.pip.common import android.platform.test.annotations.Presubmit import android.tools.common.Rotation diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt index 096af39488e9..7b7f1d7b5a82 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.pip +package com.android.wm.shell.flicker.pip.common import android.app.Instrumentation import android.content.Intent @@ -92,7 +92,7 @@ abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) { @Presubmit @Test - fun hasAtMostOnePipDismissOverlayWindow() { + open fun hasAtMostOnePipDismissOverlayWindow() { val matcher = ComponentNameMatcher("", "pip-dismiss-overlay") flicker.assertWm { val overlaysPerState = diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt index 000ae8f9458e..c6cbcd052fe0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt @@ -26,7 +26,7 @@ import com.android.server.wm.flicker.helpers.PipAppHelper /** Helper class for PIP app on AndroidTV */ open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instrumentation) { - private val appSelector = By.pkg(`package`).depth(0) + private val appSelector = By.pkg(packageName).depth(0) val ui: UiObject2? get() = uiDevice.findObject(appSelector) @@ -46,7 +46,7 @@ open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instr } override fun clickObject(resId: String) { - val selector = By.res(`package`, resId) + val selector = By.res(packageName, resId) focusOnObject(selector) || error("Could not focus on `$resId` object") uiDevice.pressDPadCenter() } @@ -68,7 +68,7 @@ open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instr } fun waitUntilClosed(): Boolean { - val appSelector = By.pkg(`package`).depth(0) + val appSelector = By.pkg(packageName).depth(0) return uiDevice.wait(Until.gone(appSelector), APP_CLOSE_WAIT_TIME_MS) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt index 610cedefe594..5f157856aa36 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service +package com.android.wm.shell.flicker.service.common import android.app.Instrumentation import android.platform.test.rule.NavigationModeRule @@ -23,6 +23,7 @@ import android.platform.test.rule.UnlockScreenRule import android.tools.common.NavBar import android.tools.common.Rotation import android.tools.device.apphelpers.MessagingAppHelper +import android.tools.device.flicker.rules.ArtifactSaverRule import android.tools.device.flicker.rules.ChangeDisplayOrientationRule import android.tools.device.flicker.rules.LaunchAppRule import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule @@ -33,9 +34,10 @@ object Utils { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() fun testSetupRule(navigationMode: NavBar, rotation: Rotation): RuleChain { - return RuleChain.outerRule(UnlockScreenRule()) + return RuleChain.outerRule(ArtifactSaverRule()) + .around(UnlockScreenRule()) .around( - NavigationModeRule(navigationMode.value, /* changeNavigationModeAfterTest */ false) + NavigationModeRule(navigationMode.value, false) ) .around( LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS new file mode 100644 index 000000000000..3ab6a1ee061d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS @@ -0,0 +1,2 @@ +# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > WM Shell > Split Screen +# Bug component: 928697 diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt index 8cb25fe531b8..69499b9b488b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt @@ -31,7 +31,8 @@ import org.junit.runner.RunWith class DismissSplitScreenByDividerGesturalNavLandscape : DismissSplitScreenByDivider(Rotation.ROTATION_90) { - @ExpectedScenarios(["SPLIT_SCREEN_EXIT"]) + // TODO(b/300260196): Not detecting SPLIT_SCREEN_EXIT right now + @ExpectedScenarios(["ENTIRE_TRACE"]) @Test override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt index fa1be63296e0..bd627f4babaa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt @@ -31,7 +31,8 @@ import org.junit.runner.RunWith class DismissSplitScreenByDividerGesturalNavPortrait : DismissSplitScreenByDivider(Rotation.ROTATION_0) { - @ExpectedScenarios(["SPLIT_SCREEN_EXIT"]) + // TODO(b/300260196): Not detecting SPLIT_SCREEN_EXIT right now + @ExpectedScenarios(["ENTIRE_TRACE"]) @Test override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt index aa35237b615f..a8f4d0a24c7e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt @@ -31,7 +31,7 @@ import org.junit.runner.RunWith class DismissSplitScreenByGoHomeGesturalNavLandscape : DismissSplitScreenByGoHome(Rotation.ROTATION_90) { - @ExpectedScenarios(["SPLIT_SCREEN_EXIT"]) + @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449) @Test override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt index e195360cdc36..cee9bbfb4aa4 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt @@ -31,7 +31,7 @@ import org.junit.runner.RunWith class DismissSplitScreenByGoHomeGesturalNavPortrait : DismissSplitScreenByGoHome(Rotation.ROTATION_0) { - @ExpectedScenarios(["SPLIT_SCREEN_EXIT"]) + @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449) @Test override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt index 5f771c7545c7..169b5cfa3462 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt @@ -31,7 +31,7 @@ import org.junit.runner.RunWith class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape : EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) { - @ExpectedScenarios(["SPLIT_SCREEN_ENTER"]) + @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332) @Test override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt index 729a401fe34c..412c011a3f17 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt @@ -31,7 +31,7 @@ import org.junit.runner.RunWith class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait : EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) { - @ExpectedScenarios(["SPLIT_SCREEN_ENTER"]) + @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332) @Test override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt index 00f607343b3b..4ff0b4362e60 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt @@ -31,7 +31,7 @@ import org.junit.runner.RunWith class SwitchBackToSplitFromRecentGesturalNavLandscape : SwitchBackToSplitFromRecent(Rotation.ROTATION_90) { - @ExpectedScenarios(["QUICKSWITCH"]) + @ExpectedScenarios(["SPLIT_SCREEN_ENTER"]) @Test override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt index b3340e77dbfa..930f31d1f348 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt @@ -31,7 +31,7 @@ import org.junit.runner.RunWith class SwitchBackToSplitFromRecentGesturalNavPortrait : SwitchBackToSplitFromRecent(Rotation.ROTATION_0) { - @ExpectedScenarios(["QUICKSWITCH"]) + @ExpectedScenarios(["SPLIT_SCREEN_ENTER"]) @Test override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt index 7cbc1c3c272c..c744103d49ba 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt @@ -16,7 +16,11 @@ package com.android.wm.shell.flicker.service.splitscreen.flicker +import android.tools.common.flicker.FlickerConfig import android.tools.common.flicker.annotation.ExpectedScenarios +import android.tools.common.flicker.annotation.FlickerConfigProvider +import android.tools.common.flicker.config.FlickerConfig +import android.tools.common.flicker.config.FlickerServiceConfig import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen import org.junit.Test @@ -25,7 +29,14 @@ import org.junit.runner.RunWith @RunWith(FlickerServiceJUnit4ClassRunner::class) class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() { - @ExpectedScenarios(["QUICKSWITCH"]) + @ExpectedScenarios(["LOCKSCREEN_UNLOCK_ANIMATION"]) @Test override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT) + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt index 2eb81e0d0492..11a4e02c5e37 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt @@ -16,7 +16,11 @@ package com.android.wm.shell.flicker.service.splitscreen.flicker +import android.tools.common.flicker.FlickerConfig import android.tools.common.flicker.annotation.ExpectedScenarios +import android.tools.common.flicker.annotation.FlickerConfigProvider +import android.tools.common.flicker.config.FlickerConfig +import android.tools.common.flicker.config.FlickerServiceConfig import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen import org.junit.Test @@ -25,7 +29,14 @@ import org.junit.runner.RunWith @RunWith(FlickerServiceJUnit4ClassRunner::class) class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() { - @ExpectedScenarios(["QUICKSWITCH"]) + @ExpectedScenarios(["LOCKSCREEN_UNLOCK_ANIMATION"]) @Test override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT) + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt index 92b62273d8cb..e37d806c7a14 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit import org.junit.Test -@RequiresDevice -class CopyContentInSplitGesturalNavPortraitBenchmark : CopyContentInSplit(Rotation.ROTATION_0) { +open class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt index 566adec75615..2a50912e0a5c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit import org.junit.Test -@RequiresDevice -class CopyContentInSplitGesturalNavLandscapeBenchmark : CopyContentInSplit(Rotation.ROTATION_90) { +open class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt index e6d56b5c94d3..d5da1a8b558c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider import org.junit.Test -@RequiresDevice -class DismissSplitScreenByDividerGesturalNavLandscapeBenchmark : +open class DismissSplitScreenByDividerGesturalNavLandscape : DismissSplitScreenByDivider(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt index 6752c58bd568..7fdcb9be62ee 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider import org.junit.Test -@RequiresDevice -class DismissSplitScreenByDividerGesturalNavPortraitBenchmark : +open class DismissSplitScreenByDividerGesturalNavPortrait : DismissSplitScreenByDivider(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt index 7c9ab9939dd0..308e954b86c1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome import org.junit.Test -@RequiresDevice -class DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark : +open class DismissSplitScreenByGoHomeGesturalNavLandscape : DismissSplitScreenByGoHome(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt index 4b795713cb23..39e75bd25a71 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome import org.junit.Test -@RequiresDevice -class DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark : +open class DismissSplitScreenByGoHomeGesturalNavPortrait : DismissSplitScreenByGoHome(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt index 71ef48bea686..e18da17175c0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize import org.junit.Test -@RequiresDevice -class DragDividerToResizeGesturalNavPortraitBenchmark : DragDividerToResize(Rotation.ROTATION_0) { +open class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt index 04950799732e..00d60e756ffa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize import org.junit.Test -@RequiresDevice -class DragDividerToResizeGesturalNavLandscapeBenchmark : DragDividerToResize(Rotation.ROTATION_90) { +open class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt index c78729c6dc92..d7efbc8c0fd4 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps import org.junit.Test -@RequiresDevice -class EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark : +open class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape : EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt index 30bce2f657b1..4eece3f62d10 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps import org.junit.Test -@RequiresDevice -class EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark : +open class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait : EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt index b33ea7c89158..d96b056d8753 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification import org.junit.Test -@RequiresDevice -class EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark : +open class EnterSplitScreenByDragFromNotificationGesturalNavLandscape : EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt index 07a86a57117b..809b690e0861 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification import org.junit.Test -@RequiresDevice -class EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark : +open class EnterSplitScreenByDragFromNotificationGesturalNavPortrait : EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt index 9a1d12787b9d..bbdf2d728494 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut import org.junit.Test -@RequiresDevice -class EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark : +open class EnterSplitScreenByDragFromShortcutGesturalNavLandscape : EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt index 266e268a3537..5c29fd8fe57e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut import org.junit.Test -@RequiresDevice -class EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark : +open class EnterSplitScreenByDragFromShortcutGesturalNavPortrait : EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt index 83fc30bceb7b..a7398ebf56e8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar import org.junit.Test -@RequiresDevice -class EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark : +open class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape : EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt index b2f19299c7f0..eae88ad4ad09 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar import org.junit.Test -@RequiresDevice -class EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark : +open class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait : EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt index dae92dddbfec..7e8ee04a28fa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview import org.junit.Test -@RequiresDevice -class EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark : +open class EnterSplitScreenFromOverviewGesturalNavLandscape : EnterSplitScreenFromOverview(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt index 732047ba38ad..9295c330b879 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview import org.junit.Test -@RequiresDevice -class EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark : +open class EnterSplitScreenFromOverviewGesturalNavPortrait : EnterSplitScreenFromOverview(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt index 1de7efd7970a..4b59e9fbd866 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider import org.junit.Test -@RequiresDevice -class SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark : +open class SwitchAppByDoubleTapDividerGesturalNavLandscape : SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt index 1a046aa5b09e..5ff36d4aabbb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider import org.junit.Test -@RequiresDevice -class SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark : +open class SwitchAppByDoubleTapDividerGesturalNavPortrait : SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt index 6e88f0eddee8..c0cb7219437b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp import org.junit.Test -@RequiresDevice -class SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark : +open class SwitchBackToSplitFromAnotherAppGesturalNavLandscape : SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt index d26a29c80583..8c140884aa50 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp import org.junit.Test -@RequiresDevice -class SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark : +open class SwitchBackToSplitFromAnotherAppGesturalNavPortrait : SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt index 4a552b0aed6a..7b6614b81c11 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome import org.junit.Test -@RequiresDevice -class SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark : +open class SwitchBackToSplitFromHomeGesturalNavLandscape : SwitchBackToSplitFromHome(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt index b7376eaea66d..5df5be9daa8b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome import org.junit.Test -@RequiresDevice -class SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark : +open class SwitchBackToSplitFromHomeGesturalNavPortrait : SwitchBackToSplitFromHome(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt index b2d05e4a2632..9d63003bf2a1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent import org.junit.Test -@RequiresDevice -class SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark : +open class SwitchBackToSplitFromRecentGesturalNavLandscape : SwitchBackToSplitFromRecent(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt index 6de31b1315e4..9fa04b208ad1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent import org.junit.Test -@RequiresDevice -class SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark : +open class SwitchBackToSplitFromRecentGesturalNavPortrait : SwitchBackToSplitFromRecent(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt index aab18a6d27b9..9386aa2b2cf0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs import org.junit.Test -@RequiresDevice -class SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark : +open class SwitchBetweenSplitPairsGesturalNavLandscape : SwitchBetweenSplitPairs(Rotation.ROTATION_90) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt index b074f2c161c9..5ef21672bfe0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs import org.junit.Test -@RequiresDevice -class SwitchBetweenSplitPairsGesturalNavPortraitBenchmark : +open class SwitchBetweenSplitPairsGesturalNavPortrait : SwitchBetweenSplitPairs(Rotation.ROTATION_0) { @PlatinumTest(focusArea = "sysui") @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt index 840401c23a91..9caab9b5182a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt @@ -14,19 +14,17 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.BlockJUnit4ClassRunner -@RequiresDevice @RunWith(BlockJUnit4ClassRunner::class) -class UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark : UnlockKeyguardToSplitScreen() { +open class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() { @PlatinumTest(focusArea = "sysui") @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt index c402aa4444d8..bf484e5cef98 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt @@ -14,19 +14,17 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.service.splitscreen.benchmark +package com.android.wm.shell.flicker.service.splitscreen.platinum import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit -import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.BlockJUnit4ClassRunner -@RequiresDevice @RunWith(BlockJUnit4ClassRunner::class) -class UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark : UnlockKeyguardToSplitScreen() { +open class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() { @PlatinumTest(focusArea = "sysui") @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt index e530f6369609..80ab24ddf9ef 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt @@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Before @@ -51,7 +51,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp, rotation) } @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt index e9fc43746d27..4c391047e853 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt @@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Before @@ -49,7 +49,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation) } @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt index 416692c37b34..f6d1afc05a5a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt @@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Before @@ -49,7 +49,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation) } @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt index 494a246d2f50..db5a32a382fb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt @@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Before @@ -49,7 +49,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation) } @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt index 369bdfc1103d..d7b306c3be23 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt @@ -19,13 +19,15 @@ package com.android.wm.shell.flicker.service.splitscreen.scenarios import android.app.Instrumentation import android.tools.common.NavBar import android.tools.common.Rotation +import android.tools.device.flicker.rules.ChangeDisplayOrientationRule import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After +import org.junit.Assume import org.junit.Before import org.junit.Ignore import org.junit.Rule @@ -46,11 +48,15 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { @Before fun setup() { - tapl.setEnableRotation(true) - tapl.setExpectedRotation(rotation.value) + Assume.assumeTrue(tapl.isTablet) tapl.goHome() + primaryApp.launchViaIntent(wmHelper) + ChangeDisplayOrientationRule.setRotation(rotation) + + tapl.setEnableRotation(true) + tapl.setExpectedRotation(rotation.value) } @Test @@ -58,7 +64,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { tapl.launchedAppState.taskbar .openAllApps() .getAppIcon(secondaryApp.appName) - .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName) SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt index 776c397cc354..cc982d1ba860 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt @@ -19,13 +19,15 @@ package com.android.wm.shell.flicker.service.splitscreen.scenarios import android.app.Instrumentation import android.tools.common.NavBar import android.tools.common.Rotation +import android.tools.device.flicker.rules.ChangeDisplayOrientationRule import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After +import org.junit.Assume import org.junit.Before import org.junit.Ignore import org.junit.Rule @@ -47,14 +49,18 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { @Before fun setup() { - tapl.setEnableRotation(true) - tapl.setExpectedRotation(rotation.value) + Assume.assumeTrue(tapl.isTablet) // Send a notification sendNotificationApp.launchViaIntent(wmHelper) sendNotificationApp.postNotification(wmHelper) tapl.goHome() + + tapl.setEnableRotation(true) + tapl.setExpectedRotation(rotation.value) + primaryApp.launchViaIntent(wmHelper) + ChangeDisplayOrientationRule.setRotation(rotation) } @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt index 5d67dc7e231b..fa12bb869467 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt @@ -19,11 +19,12 @@ package com.android.wm.shell.flicker.service.splitscreen.scenarios import android.app.Instrumentation import android.tools.common.NavBar import android.tools.common.Rotation +import android.tools.device.flicker.rules.ChangeDisplayOrientationRule import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Assume @@ -49,12 +50,13 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { fun setup() { Assume.assumeTrue(tapl.isTablet) - tapl.setEnableRotation(true) - tapl.setExpectedRotation(rotation.value) - tapl.goHome() SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName) primaryApp.launchViaIntent(wmHelper) + ChangeDisplayOrientationRule.setRotation(rotation) + + tapl.setEnableRotation(true) + tapl.setExpectedRotation(rotation.value) } @Test @@ -63,7 +65,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { .getAppIcon(secondaryApp.appName) .openDeepShortcutMenu() .getMenuItem("Split Screen Secondary Activity") - .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName) SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) // TODO: Do we want this check in here? Add to the other tests? diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt index 5bbb42fd1864..2592fd40d902 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt @@ -23,9 +23,10 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After +import org.junit.Assume import org.junit.Before import org.junit.Ignore import org.junit.Rule @@ -46,6 +47,8 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { @Before fun setup() { + Assume.assumeTrue(tapl.isTablet) + tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) @@ -58,7 +61,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { open fun enterSplitScreenByDragFromTaskbar() { tapl.launchedAppState.taskbar .getAppIcon(secondaryApp.appName) - .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName) SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt index c2100f641a55..983653b9b5ca 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt @@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Before @@ -46,9 +46,6 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { @Before fun setup() { - tapl.setEnableRotation(true) - tapl.setExpectedRotation(rotation.value) - primaryApp.launchViaIntent(wmHelper) secondaryApp.launchViaIntent(wmHelper) tapl.goHome() @@ -57,11 +54,14 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { .withAppTransitionIdle() .withHomeActivityVisible() .waitForAndVerify() + + tapl.setEnableRotation(true) + tapl.setExpectedRotation(rotation.value) } @Test open fun enterSplitScreenFromOverview() { - SplitScreenUtils.splitFromOverview(tapl, device) + SplitScreenUtils.splitFromOverview(tapl, device, rotation) SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt index 70f3bed9afdc..068171d2e129 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt @@ -25,7 +25,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Before @@ -48,11 +48,12 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { @Before fun setup() { + tapl.workspace.switchToOverview().dismissAllTasks() + tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) - tapl.workspace.switchToOverview().dismissAllTasks() - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation) } @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt index 86f394da0231..64b75c5fd967 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt @@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Before @@ -50,7 +50,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation) thirdApp.launchViaIntent(wmHelper) wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt index d7b611e04d9d..179501089168 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt @@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Before @@ -49,7 +49,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation) tapl.goHome() wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt index bf4c381b1c3a..7065846dc653 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt @@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Before @@ -46,10 +46,12 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { @Before fun setup() { + tapl.workspace.switchToOverview().dismissAllTasks() + tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation) tapl.goHome() wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt index 4a9c32f10415..251cb50de017 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt @@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Before @@ -51,8 +51,8 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) { tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp, rotation) SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt index 383a6b39a2b6..a9933bbe09fc 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt @@ -23,7 +23,7 @@ import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.wm.shell.flicker.service.Utils +import com.android.wm.shell.flicker.service.common.Utils import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.After import org.junit.Before diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt index 3702be9541a3..6b971699d212 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.common.traces.component.EdgeExtensionComponentMatcher @@ -24,6 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.CopyContentInSplitBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt index 8b906305506f..51588569a8aa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt @@ -16,13 +16,13 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.helpers.WindowUtils +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByDividerBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt index 50f6a382a702..fc6c2b3d7ce7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt @@ -16,12 +16,12 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByGoHomeBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt index ca9c13009848..8b1689a9d816 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt @@ -16,12 +16,12 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.DragDividerToResizeBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions @@ -57,7 +57,7 @@ class DragDividerToResize(override val flicker: LegacyFlickerTest) : @Test fun splitScreenDividerKeepVisible() = flicker.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) - @Presubmit + @FlakyTest(bugId = 291678271) @Test fun primaryAppLayerVisibilityChanges() { flicker.assertLayers { @@ -69,7 +69,7 @@ class DragDividerToResize(override val flicker: LegacyFlickerTest) : } } - @Presubmit + @FlakyTest(bugId = 291678271) @Test fun secondaryAppLayerVisibilityChanges() { flicker.assertLayers { @@ -87,7 +87,7 @@ class DragDividerToResize(override val flicker: LegacyFlickerTest) : @Test fun secondaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(secondaryApp) - @FlakyTest(bugId = 245472831) + @FlakyTest(bugId = 291678271) @Test fun primaryAppBoundsChanges() { flicker.splitAppLayerBoundsChanges( @@ -97,7 +97,7 @@ class DragDividerToResize(override val flicker: LegacyFlickerTest) : ) } - @Presubmit + @FlakyTest(bugId = 291678271) @Test fun secondaryAppBoundsChanges() = flicker.splitAppLayerBoundsChanges( @@ -106,6 +106,12 @@ class DragDividerToResize(override val flicker: LegacyFlickerTest) : portraitPosTop = true ) + @FlakyTest(bugId = 291678271) + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { + super.visibleLayersShownMoreThanOneConsecutiveEntry() + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt index f8d1e1f1f498..99613f39060d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.NavBar @@ -24,6 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromAllAppsBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt index ff5d93550541..756a7fa4ba98 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.NavBar @@ -24,6 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromNotificationBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt index 7c710777087d..121b46acdb66 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt @@ -16,13 +16,13 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromShortcutBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt index 83717062b05e..99deb9279271 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.NavBar @@ -24,6 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromTaskbarBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt index 0bfdbb4de7c5..212a4e3649dc 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt @@ -16,12 +16,12 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenFromOverviewBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS new file mode 100644 index 000000000000..3ab6a1ee061d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS @@ -0,0 +1,2 @@ +# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > WM Shell > Split Screen +# Bug component: 928697 diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt index 88bbc0e7880b..284c32ea110d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt @@ -16,13 +16,13 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromAnotherAppBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt index e85dc24a7781..9e6448f0bec9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt @@ -16,13 +16,13 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromHomeBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt index f7a9ed073002..8e28712cd993 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt @@ -16,13 +16,13 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromRecentBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt index 66f9b85ea572..fb0193b1830d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt @@ -16,12 +16,12 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBetweenSplitPairsBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt new file mode 100644 index 000000000000..13875362a1c8 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen + +import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.common.traces.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.helpers.PipAppHelper +import com.android.wm.shell.flicker.splitscreen.benchmark.SplitScreenBase +import com.android.wm.shell.flicker.utils.SplitScreenUtils +import com.android.wm.shell.flicker.utils.layerBecomesInvisible +import com.android.wm.shell.flicker.utils.splitAppLayerBoundsIsVisibleAtEnd +import com.android.wm.shell.flicker.utils.splitAppLayerBoundsSnapToDivider +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test quick switch between two split pairs. + * + * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBetweenSplitPairsNoPip` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class SwitchBetweenSplitPairsNoPip(override val flicker: LegacyFlickerTest) : + SplitScreenBase(flicker) { + + val thirdApp = SplitScreenUtils.getSendNotification(instrumentation) + val pipApp = PipAppHelper(instrumentation) + + override val transition: FlickerBuilder.() -> Unit + get() = { + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + tapl.goHome() + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, + secondaryApp, flicker.scenario.startRotation) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, pipApp, + flicker.scenario.startRotation) + pipApp.enableAutoEnterForPipActivity() + SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, pipApp) + } + transitions { + tapl.launchedAppState.quickSwitchToPreviousApp() + SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + teardown { + pipApp.exit(wmHelper) + thirdApp.exit(wmHelper) + } + } + + /** Checks that [pipApp] window won't enter pip */ + @Presubmit + @Test + fun notEnterPip() { + flicker.assertWm { isNotPinned(pipApp) } + } + + /** Checks the [pipApp] task did not reshow during transition. */ + @Presubmit + @Test + fun app1WindowIsVisibleOnceApp2WindowIsInvisible() { + flicker.assertLayers { + this.isVisible(pipApp) + .then() + .isVisible(ComponentNameMatcher.LAUNCHER, isOptional = true) + .then() + .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) + .then() + .isInvisible(pipApp) + .isVisible(secondaryApp) + } + } + + @Presubmit + @Test + fun primaryAppBoundsIsVisibleAtEnd() = + flicker.splitAppLayerBoundsIsVisibleAtEnd( + primaryApp, + landscapePosLeft = tapl.isTablet, + portraitPosTop = false + ) + + @Presubmit + @Test + fun secondaryAppBoundsIsVisibleAtEnd() = + flicker.splitAppLayerBoundsIsVisibleAtEnd( + secondaryApp, + landscapePosLeft = !tapl.isTablet, + portraitPosTop = true + ) + + /** Checks the [pipApp] task become invisible after transition finish. */ + @Presubmit @Test fun pipAppLayerBecomesInvisible() = flicker.layerBecomesInvisible(pipApp) + + /** Checks the [pipApp] task is in split screen bounds when transition start. */ + @Presubmit + @Test + fun pipAppBoundsIsVisibleAtBegin() = + flicker.assertLayersStart { + this.splitAppLayerBoundsSnapToDivider( + pipApp, + landscapePosLeft = !tapl.isTablet, + portraitPosTop = true, + flicker.scenario.startRotation + ) + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt index 851391d37323..f3145c97a6f1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt @@ -17,12 +17,16 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit import android.tools.common.NavBar +import android.tools.common.flicker.subject.layers.LayersTraceSubject import android.tools.common.flicker.subject.region.RegionSubject +import android.tools.common.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.benchmark.UnlockKeyguardToSplitScreenBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions @@ -57,6 +61,22 @@ class UnlockKeyguardToSplitScreen(override val flicker: LegacyFlickerTest) : } @Test + @FlakyTest(bugId = 293578017) + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() + + // TODO(b/293578017) remove once that bug is resolve + @Test + @Presubmit + fun visibleLayersShownMoreThanOneConsecutiveEntry_withoutWallpaper() = + flicker.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry( + LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + + listOf(WALLPAPER_BBQ_WRAPPER) + ) + } + + @Test fun splitScreenDividerIsVisibleAtEnd() { flicker.assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt index e5c1e75a75f4..3b9e53f9ce04 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -40,7 +39,8 @@ abstract class CopyContentInSplitBenchmark(override val flicker: LegacyFlickerTe protected val popupWindowLayer = ComponentNameMatcher("", "PopupWindow:") protected val thisTransition: FlickerBuilder.() -> Unit get() = { - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp) } + setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, + textEditApp, flicker.scenario.startRotation) } transitions { SplitScreenUtils.copyContentInSplit( instrumentation, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt index e4e1af9d24ce..5fdde3ad23d2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt @@ -21,7 +21,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -36,7 +35,8 @@ abstract class DismissSplitScreenByDividerBenchmark(override val flicker: Legacy SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } + setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, + secondaryApp, flicker.scenario.startRotation) } transitions { if (tapl.isTablet) { SplitScreenUtils.dragDividerToDismissSplit( diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt index b2dd02bf2c41..b7f6bfe7efd6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt @@ -21,7 +21,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -36,7 +35,10 @@ abstract class DismissSplitScreenByGoHomeBenchmark(override val flicker: LegacyF SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } + setup { + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, + flicker.scenario.startRotation) + } transitions { tapl.goHome() wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt index 078859166dbc..bb2a7aabed1b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt @@ -21,7 +21,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.Assume import org.junit.Before @@ -38,7 +37,8 @@ abstract class DragDividerToResizeBenchmark(override val flicker: LegacyFlickerT SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } + setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, + secondaryApp, flicker.scenario.startRotation) } transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt index 884e4513e893..394864ad9d4d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.Assume import org.junit.Before @@ -48,7 +47,7 @@ abstract class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: tapl.launchedAppState.taskbar .openAllApps() .getAppIcon(secondaryApp.appName) - .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName) SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt index e5c40b69726c..cd3fbab1497b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.Assume import org.junit.Before diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt index 04510014c437..3b3be84f9841 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.Assume import org.junit.Before @@ -54,7 +53,7 @@ abstract class EnterSplitScreenByDragFromShortcutBenchmark( .getAppIcon(secondaryApp.appName) .openDeepShortcutMenu() .getMenuItem("Split Screen Secondary Activity") - .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName) SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt index 9e0ca1b20f09..eff355987cc0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.Assume import org.junit.Before @@ -47,7 +46,7 @@ abstract class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: transitions { tapl.launchedAppState.taskbar .getAppIcon(secondaryApp.appName) - .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName) SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt index 06b4fe7e0eb4..5e5e7d7fc3c9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt @@ -21,7 +21,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -47,7 +46,7 @@ abstract class EnterSplitScreenFromOverviewBenchmark(override val flicker: Legac .waitForAndVerify() } transitions { - SplitScreenUtils.splitFromOverview(tapl, device) + SplitScreenUtils.splitFromOverview(tapl, device, flicker.scenario.startRotation) SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt index 7ce995ac64aa..a0e437c25aa7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.splitscreen +package com.android.wm.shell.flicker.splitscreen.benchmark import android.content.Context import android.tools.device.flicker.legacy.FlickerBuilder @@ -33,7 +33,10 @@ abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(f tapl.setEnableRotation(true) setRotation(flicker.scenario.startRotation) tapl.setExpectedRotation(flicker.scenario.startRotation.value) - tapl.workspace.switchToOverview().dismissAllTasks() + val overview = tapl.workspace.switchToOverview() + if (overview.hasTasks()) { + overview.dismissAllTasks() + } } } @@ -43,11 +46,4 @@ abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(f secondaryApp.exit(wmHelper) } } - - protected open val withoutTracing: FlickerBuilder.() -> Unit = { - withoutLayerTracing() - withoutWindowManagerTracing() - withoutTransitionTracing() - withoutTransactionsTracing() - } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt index 007b7518b16e..46b0bd226daf 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt @@ -25,7 +25,6 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -40,7 +39,8 @@ abstract class SwitchAppByDoubleTapDividerBenchmark(override val flicker: Legacy SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } + setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, + secondaryApp, flicker.scenario.startRotation) } transitions { SplitScreenUtils.doubleTapDividerToSwitch(device) wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt index 10c8eebf7d1d..baf76932c7ac 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -40,7 +39,8 @@ abstract class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: Le protected val thisTransition: FlickerBuilder.() -> Unit get() = { setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, + secondaryApp, flicker.scenario.startRotation) thirdApp.launchViaIntent(wmHelper) wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt index a6e750fed70e..33b55f1a57d8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -38,7 +37,8 @@ abstract class SwitchBackToSplitFromHomeBenchmark(override val flicker: LegacyFl protected val thisTransition: FlickerBuilder.() -> Unit get() = { setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, + secondaryApp, flicker.scenario.startRotation) tapl.goHome() wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt index 7e8d5fb83157..b79dfb5f0665 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -38,7 +37,8 @@ abstract class SwitchBackToSplitFromRecentBenchmark(override val flicker: Legacy protected val thisTransition: FlickerBuilder.() -> Unit get() = { setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, + secondaryApp, flicker.scenario.startRotation) tapl.goHome() wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt index 56edad1ded19..0204d754585a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt @@ -21,7 +21,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -40,8 +39,10 @@ abstract class SwitchBetweenSplitPairsBenchmark(override val flicker: LegacyFlic protected val thisTransition: FlickerBuilder.() -> Unit get() = { setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, + secondaryApp, flicker.scenario.startRotation) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp, + flicker.scenario.startRotation) SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp) } transitions { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt index 065d4d62be42..e71834de7123 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.utils.SplitScreenUtils import org.junit.FixMethodOrder import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt index e5c124cbe775..f1cb37ee1293 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt @@ -265,6 +265,7 @@ fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider( val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT)?.visibleRegion?.region ?: error("$SPLIT_SCREEN_DIVIDER_COMPONENT component not found") + visibleRegion(component).isNotEmpty() visibleRegion(component) .coversAtMost( if (displayBounds.width > displayBounds.height) { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt index 3f8a1ae6bd79..6b3cfaf33c05 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt @@ -19,6 +19,7 @@ package com.android.wm.shell.flicker.utils import android.app.Instrumentation import android.graphics.Point import android.os.SystemClock +import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.common.traces.component.IComponentMatcher import android.tools.common.traces.component.IComponentNameMatcher @@ -41,6 +42,7 @@ import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary import org.junit.Assert.assertNotNull +import android.tools.device.flicker.rules.ChangeDisplayOrientationRule object SplitScreenUtils { private const val TIMEOUT_MS = 3_000L @@ -101,13 +103,15 @@ object SplitScreenUtils { tapl: LauncherInstrumentation, device: UiDevice, primaryApp: StandardAppHelper, - secondaryApp: StandardAppHelper + secondaryApp: StandardAppHelper, + rotation: Rotation ) { primaryApp.launchViaIntent(wmHelper) secondaryApp.launchViaIntent(wmHelper) + ChangeDisplayOrientationRule.setRotation(rotation) tapl.goHome() wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() - splitFromOverview(tapl, device) + splitFromOverview(tapl, device, rotation) waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } @@ -121,7 +125,7 @@ object SplitScreenUtils { waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } - fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) { + fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice, rotation: Rotation) { // Note: The initial split position in landscape is different between tablet and phone. // In landscape, tablet will let the first app split to right side, and phone will // split to left side. @@ -129,7 +133,9 @@ object SplitScreenUtils { // TAPL's currentTask on tablet is sometimes not what we expected if the overview // contains more than 3 task views. We need to use uiautomator directly to find the // second task to split. - tapl.workspace.switchToOverview().overviewActions.clickSplit() + val home = tapl.workspace.switchToOverview() + ChangeDisplayOrientationRule.setRotation(rotation) + home.overviewActions.clickSplit() val snapshots = device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS) if (snapshots == null || snapshots.size < 1) { error("Fail to find a overview snapshot to split.") @@ -145,9 +151,13 @@ object SplitScreenUtils { } snapshots[0].click() } else { - tapl.workspace + val rotationCheckEnabled = tapl.getExpectedRotationCheckEnabled() + tapl.setExpectedRotationCheckEnabled(false) // disable rotation check to enter overview + val home = tapl.workspace .switchToOverview() - .currentTask + tapl.setExpectedRotationCheckEnabled(rotationCheckEnabled) // restore rotation checks + ChangeDisplayOrientationRule.setRotation(rotation) + home.currentTask .tapMenu() .tapSplitMenuItem() .currentTask diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index d09a90cd7dc7..aadadd604d3e 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -35,6 +35,7 @@ android_test { static_libs: [ "WindowManager-Shell", "junit", + "flag-junit-base", "androidx.test.runner", "androidx.test.rules", "androidx.test.ext.junit", @@ -49,6 +50,7 @@ android_test { "testables", "platform-test-annotations", "servicestests-utils", + "com_android_wm_shell_flags_lib", ], libs: [ diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java index 2d9304705738..02c9d306f4bf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java @@ -35,7 +35,7 @@ import android.window.TransitionInfo; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.wm.shell.TransitionInfoBuilder; +import com.android.wm.shell.transition.TransitionInfoBuilder; import org.junit.Before; import org.junit.Test; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java index 270dbc49835f..83d9f654376c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java @@ -39,7 +39,7 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.wm.shell.TransitionInfoBuilder; +import com.android.wm.shell.transition.TransitionInfoBuilder; import org.junit.Before; import org.junit.Test; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt index 17ed396987af..e7274918fa2b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt @@ -424,6 +424,7 @@ class PhysicsAnimatorTest : ShellTestCase() { eq(-5f), anyFloat(), eq(true)) } + @Ignore("Started flaking despite no changes, tracking in b/299636216") @Test fun testIsPropertyAnimating() { PhysicsAnimatorTestUtils.setAllAnimationsBlock(false) 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 3d8bd3854a45..e7d0f601ff5a 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 @@ -67,6 +67,7 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.sysui.ShellSharedConstants; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -85,12 +86,11 @@ public class BackAnimationControllerTest extends ShellTestCase { private static final String ANIMATION_ENABLED = "1"; private final TestShellExecutor mShellExecutor = new TestShellExecutor(); - private ShellInit mShellInit; - @Rule public TestableContext mContext = new TestableContext(InstrumentationRegistry.getInstrumentation().getContext()); + private ShellInit mShellInit; @Mock private IActivityTaskManager mActivityTaskManager; @@ -116,6 +116,8 @@ public class BackAnimationControllerTest extends ShellTestCase { private TestableContentResolver mContentResolver; private TestableLooper mTestableLooper; + private ShellBackAnimationRegistry mShellBackAnimationRegistry; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -126,11 +128,23 @@ public class BackAnimationControllerTest extends ShellTestCase { ANIMATION_ENABLED); mTestableLooper = TestableLooper.get(this); mShellInit = spy(new ShellInit(mShellExecutor)); - mController = new BackAnimationController(mShellInit, mShellController, - mShellExecutor, new Handler(mTestableLooper.getLooper()), - mActivityTaskManager, mContext, - mContentResolver, mAnimationBackground); - mController.setEnableUAnimation(true); + mShellBackAnimationRegistry = + new ShellBackAnimationRegistry( + new CrossActivityAnimation(mContext, mAnimationBackground), + new CrossTaskBackAnimation(mContext, mAnimationBackground), + new CustomizeActivityAnimation(mContext, mAnimationBackground), + null); + mController = + new BackAnimationController( + mShellInit, + mShellController, + mShellExecutor, + new Handler(mTestableLooper.getLooper()), + mActivityTaskManager, + mContext, + mContentResolver, + mAnimationBackground, + mShellBackAnimationRegistry); mShellInit.init(); mShellExecutor.flushAll(); } @@ -138,12 +152,13 @@ public class BackAnimationControllerTest extends ShellTestCase { private void createNavigationInfo(int backType, boolean enableAnimation, boolean isAnimationCallback) { - BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder() - .setType(backType) - .setOnBackNavigationDone(new RemoteCallback((bundle) -> {})) - .setOnBackInvokedCallback(mAppCallback) - .setPrepareRemoteAnimation(enableAnimation) - .setAnimationCallback(isAnimationCallback); + BackNavigationInfo.Builder builder = + new BackNavigationInfo.Builder() + .setType(backType) + .setOnBackNavigationDone(new RemoteCallback((bundle) -> {})) + .setOnBackInvokedCallback(mAppCallback) + .setPrepareRemoteAnimation(enableAnimation) + .setAnimationCallback(isAnimationCallback); createNavigationInfo(builder); } @@ -188,18 +203,21 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void verifyNavigationFinishes() throws RemoteException { - final int[] testTypes = new int[] {BackNavigationInfo.TYPE_RETURN_TO_HOME, - BackNavigationInfo.TYPE_CROSS_TASK, - BackNavigationInfo.TYPE_CROSS_ACTIVITY, - BackNavigationInfo.TYPE_DIALOG_CLOSE, - BackNavigationInfo.TYPE_CALLBACK }; - - for (int type: testTypes) { + final int[] testTypes = + new int[] { + BackNavigationInfo.TYPE_RETURN_TO_HOME, + BackNavigationInfo.TYPE_CROSS_TASK, + BackNavigationInfo.TYPE_CROSS_ACTIVITY, + BackNavigationInfo.TYPE_DIALOG_CLOSE, + BackNavigationInfo.TYPE_CALLBACK + }; + + for (int type : testTypes) { registerAnimation(type); } - for (int type: testTypes) { - final ResultListener result = new ResultListener(); + for (int type : testTypes) { + final ResultListener result = new ResultListener(); createNavigationInfo(new BackNavigationInfo.Builder() .setType(type) .setOnBackInvokedCallback(mAppCallback) @@ -275,10 +293,17 @@ public class BackAnimationControllerTest extends ShellTestCase { // Toggle the setting off Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0"); ShellInit shellInit = new ShellInit(mShellExecutor); - mController = new BackAnimationController(shellInit, mShellController, - mShellExecutor, new Handler(mTestableLooper.getLooper()), - mActivityTaskManager, mContext, - mContentResolver, mAnimationBackground); + mController = + new BackAnimationController( + shellInit, + mShellController, + mShellExecutor, + new Handler(mTestableLooper.getLooper()), + mActivityTaskManager, + mContext, + mContentResolver, + mAnimationBackground, + mShellBackAnimationRegistry); shellInit.init(); registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); @@ -398,17 +423,19 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void animationNotDefined() throws RemoteException { - final int[] testTypes = new int[] { - BackNavigationInfo.TYPE_RETURN_TO_HOME, - BackNavigationInfo.TYPE_CROSS_TASK, - BackNavigationInfo.TYPE_CROSS_ACTIVITY, - BackNavigationInfo.TYPE_DIALOG_CLOSE}; - - for (int type: testTypes) { + final int[] testTypes = + new int[] { + BackNavigationInfo.TYPE_RETURN_TO_HOME, + BackNavigationInfo.TYPE_CROSS_TASK, + BackNavigationInfo.TYPE_CROSS_ACTIVITY, + BackNavigationInfo.TYPE_DIALOG_CLOSE + }; + + for (int type : testTypes) { unregisterAnimation(type); } - for (int type: testTypes) { + for (int type : testTypes) { final ResultListener result = new ResultListener(); createNavigationInfo(new BackNavigationInfo.Builder() .setType(type) @@ -468,16 +495,14 @@ public class BackAnimationControllerTest extends ShellTestCase { public void testBackToActivity() throws RemoteException { final CrossActivityAnimation animation = new CrossActivityAnimation(mContext, mAnimationBackground); - verifySystemBackBehavior( - BackNavigationInfo.TYPE_CROSS_ACTIVITY, animation.mBackAnimationRunner); + verifySystemBackBehavior(BackNavigationInfo.TYPE_CROSS_ACTIVITY, animation.getRunner()); } @Test public void testBackToTask() throws RemoteException { final CrossTaskBackAnimation animation = new CrossTaskBackAnimation(mContext, mAnimationBackground); - verifySystemBackBehavior( - BackNavigationInfo.TYPE_CROSS_TASK, animation.mBackAnimationRunner); + verifySystemBackBehavior(BackNavigationInfo.TYPE_CROSS_TASK, animation.getRunner()); } private void verifySystemBackBehavior(int type, BackAnimationRunner animation) @@ -554,10 +579,12 @@ public class BackAnimationControllerTest extends ShellTestCase { private static class ResultListener implements RemoteCallback.OnResultListener { boolean mBackNavigationDone = false; boolean mTriggerBack = false; + @Override public void onResult(@Nullable Bundle result) { mBackNavigationDone = true; mTriggerBack = result.getBoolean(KEY_TRIGGER_BACK); } - }; + } + ; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java index e7d459893ce8..cebbbd890f05 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java @@ -102,15 +102,17 @@ public class CustomizeActivityAnimationTest extends ShellTestCase { // start animation with remote animation targets final CountDownLatch finishCalled = new CountDownLatch(1); final Runnable finishCallback = finishCalled::countDown; - mCustomizeActivityAnimation.mBackAnimationRunner.startAnimation( - new RemoteAnimationTarget[]{close, open}, null, null, finishCallback); + mCustomizeActivityAnimation + .getRunner() + .startAnimation( + new RemoteAnimationTarget[] {close, open}, null, null, finishCallback); verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE)); verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE)); try { - mCustomizeActivityAnimation.mBackAnimationRunner.getCallback().onBackInvoked(); + mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked(); } catch (RemoteException r) { fail("onBackInvoked throw remote exception"); } @@ -133,15 +135,17 @@ public class CustomizeActivityAnimationTest extends ShellTestCase { // start animation with remote animation targets final CountDownLatch finishCalled = new CountDownLatch(1); final Runnable finishCallback = finishCalled::countDown; - mCustomizeActivityAnimation.mBackAnimationRunner.startAnimation( - new RemoteAnimationTarget[]{close, open}, null, null, finishCallback); + mCustomizeActivityAnimation + .getRunner() + .startAnimation( + new RemoteAnimationTarget[] {close, open}, null, null, finishCallback); verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE)); verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE)); try { - mCustomizeActivityAnimation.mBackAnimationRunner.getCallback().onBackCancelled(); + mCustomizeActivityAnimation.getRunner().getCallback().onBackCancelled(); } catch (RemoteException r) { fail("onBackCancelled throw remote exception"); } @@ -155,11 +159,12 @@ public class CustomizeActivityAnimationTest extends ShellTestCase { // start animation without any remote animation targets final CountDownLatch finishCalled = new CountDownLatch(1); final Runnable finishCallback = finishCalled::countDown; - mCustomizeActivityAnimation.mBackAnimationRunner.startAnimation( - new RemoteAnimationTarget[]{}, null, null, finishCallback); + mCustomizeActivityAnimation + .getRunner() + .startAnimation(new RemoteAnimationTarget[] {}, null, null, finishCallback); try { - mCustomizeActivityAnimation.mBackAnimationRunner.getCallback().onBackInvoked(); + mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked(); } catch (RemoteException r) { fail("onBackInvoked throw remote exception"); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index 4a55429eacb6..26c73946c1c6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -111,6 +111,8 @@ public class BubbleDataTest extends ShellTestCase { @Mock private BubbleLogger mBubbleLogger; @Mock + private BubbleEducationController mEducationController; + @Mock private ShellExecutor mMainExecutor; @Captor @@ -191,7 +193,7 @@ public class BubbleDataTest extends ShellTestCase { mPositioner = new TestableBubblePositioner(mContext, mock(WindowManager.class)); - mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner, + mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner, mEducationController, mMainExecutor); // Used by BubbleData to set lastAccessedTime @@ -385,6 +387,65 @@ public class BubbleDataTest extends ShellTestCase { assertOverflowChangedTo(ImmutableList.of()); } + /** + * Verifies that the update shouldn't show the user education, if the education is not required + */ + @Test + public void test_shouldNotShowEducation() { + // Setup + when(mEducationController.shouldShowStackEducation(any())).thenReturn(false); + mBubbleData.setListener(mListener); + + // Test + mBubbleData.notificationEntryUpdated(mBubbleA1, /* suppressFlyout */ true, /* showInShade */ + true); + + // Verify + verifyUpdateReceived(); + BubbleData.Update update = mUpdateCaptor.getValue(); + assertThat(update.shouldShowEducation).isFalse(); + } + + /** + * Verifies that the update should show the user education, if the education is required + */ + @Test + public void test_shouldShowEducation() { + // Setup + when(mEducationController.shouldShowStackEducation(any())).thenReturn(true); + mBubbleData.setListener(mListener); + + // Test + mBubbleData.notificationEntryUpdated(mBubbleA1, /* suppressFlyout */ true, /* showInShade */ + true); + + // Verify + verifyUpdateReceived(); + BubbleData.Update update = mUpdateCaptor.getValue(); + assertThat(update.shouldShowEducation).isTrue(); + } + + /** + * Verifies that the update shouldn't show the user education, if the education is required but + * the bubble should auto-expand + */ + @Test + public void test_shouldShowEducation_shouldAutoExpand() { + // Setup + when(mEducationController.shouldShowStackEducation(any())).thenReturn(true); + mBubbleData.setListener(mListener); + mBubbleA1.setShouldAutoExpand(true); + + // Test + mBubbleData.notificationEntryUpdated(mBubbleA1, /* suppressFlyout */ true, /* showInShade */ + true); + + // Verify + verifyUpdateReceived(); + BubbleData.Update update = mUpdateCaptor.getValue(); + assertThat(update.shouldShowEducation).isFalse(); + } + // COLLAPSED / ADD /** diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java index 0dc16f44340f..cb29a21e2f5b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell; +package com.android.wm.shell.bubbles; import static com.google.common.truth.Truth.assertThat; @@ -27,10 +27,7 @@ import android.view.WindowManager; import androidx.test.filters.SmallTest; -import com.android.wm.shell.bubbles.BubbleController; -import com.android.wm.shell.bubbles.BubbleOverflow; -import com.android.wm.shell.bubbles.BubbleStackView; -import com.android.wm.shell.bubbles.TestableBubblePositioner; +import com.android.wm.shell.ShellTestCase; import org.junit.Before; import org.junit.Test; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java index 58d9a6486ff2..287a97c9b5b0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java @@ -22,20 +22,24 @@ import static android.view.View.LAYOUT_DIRECTION_LTR; import static android.view.View.LAYOUT_DIRECTION_RTL; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.content.Intent; import android.content.res.Configuration; import android.graphics.Insets; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; +import android.os.UserHandle; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; +import android.util.DisplayMetrics; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowMetrics; @@ -257,6 +261,27 @@ public class BubblePositionerTest extends ShellTestCase { assertThat(mPositioner.hasUserModifiedDefaultPosition()).isTrue(); } + @Test + public void testExpandedViewHeight_onLargeTablet() { + Insets insets = Insets.of(10, 20, 5, 15); + Rect screenBounds = new Rect(0, 0, 1800, 2600); + + new WindowManagerConfig() + .setLargeScreen() + .setInsets(insets) + .setScreenBounds(screenBounds) + .setUpConfig(); + mPositioner.update(); + + Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); + Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); + + int manageButtonHeight = + mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height); + float expectedHeight = 1800 - 2 * 20 - manageButtonHeight; + assertThat(mPositioner.getExpandedViewHeight(bubble)).isWithin(0.1f).of(expectedHeight); + } + /** * Calculates the Y position bubbles should be placed based on the config. Based on * the calculations in {@link BubblePositioner#getDefaultStartPosition()} and @@ -323,6 +348,8 @@ public class BubblePositionerTest extends ShellTestCase { ? MIN_WIDTH_FOR_TABLET : MIN_WIDTH_FOR_TABLET - 1; mConfiguration.orientation = mOrientation; + mConfiguration.screenWidthDp = pxToDp(mScreenBounds.width()); + mConfiguration.screenHeightDp = pxToDp(mScreenBounds.height()); when(mConfiguration.getLayoutDirection()).thenReturn(mLayoutDirection); WindowInsets windowInsets = mock(WindowInsets.class); @@ -331,5 +358,11 @@ public class BubblePositionerTest extends ShellTestCase { when(mWindowMetrics.getBounds()).thenReturn(mScreenBounds); when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics); } + + private int pxToDp(float px) { + int dpi = mContext.getResources().getDisplayMetrics().densityDpi; + float dp = px / ((float) dpi / DisplayMetrics.DENSITY_DEFAULT); + return (int) dp; + } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt new file mode 100644 index 000000000000..f58332198696 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.bubbles + +import android.content.pm.LauncherApps +import android.content.pm.ShortcutInfo +import android.content.res.Resources +import android.graphics.Color +import android.os.Handler +import android.os.UserManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.view.IWindowManager +import android.view.WindowManager +import androidx.test.filters.SmallTest +import com.android.internal.R +import com.android.internal.statusbar.IStatusBarService +import com.android.launcher3.icons.BubbleIconFactory +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.TestShellExecutor +import com.android.wm.shell.WindowManagerShellWrapper +import com.android.wm.shell.bubbles.bar.BubbleBarLayerView +import com.android.wm.shell.bubbles.properties.BubbleProperties +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.FloatingContentCoordinator +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.common.TaskStackListenerImpl +import com.android.wm.shell.sysui.ShellCommandHandler +import com.android.wm.shell.sysui.ShellController +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.taskview.TaskViewTransitions +import com.android.wm.shell.transition.Transitions +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +/** + * Tests for loading / inflating views & icons for a bubble. + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +class BubbleViewInfoTest : ShellTestCase() { + + private lateinit var metadataFlagListener: Bubbles.BubbleMetadataFlagListener + private lateinit var iconFactory: BubbleIconFactory + private lateinit var bubble: Bubble + + private lateinit var bubbleController: BubbleController + private lateinit var mainExecutor: ShellExecutor + private lateinit var bubbleStackView: BubbleStackView + private lateinit var bubbleBarLayerView: BubbleBarLayerView + + @Before + fun setup() { + metadataFlagListener = Bubbles.BubbleMetadataFlagListener {} + iconFactory = BubbleIconFactory(context, + 60, + 30, + Color.RED, + mContext.resources.getDimensionPixelSize( + R.dimen.importance_ring_stroke_width)) + + mainExecutor = TestShellExecutor() + val windowManager = context.getSystemService(WindowManager::class.java) + val shellInit = ShellInit(mainExecutor) + val shellCommandHandler = ShellCommandHandler() + val shellController = ShellController(context, shellInit, shellCommandHandler, + mainExecutor) + val bubblePositioner = BubblePositioner(context, windowManager) + val bubbleData = BubbleData(context, mock<BubbleLogger>(), bubblePositioner, + BubbleEducationController(context), mainExecutor) + val surfaceSynchronizer = { obj: Runnable -> obj.run() } + + bubbleController = BubbleController( + context, + shellInit, + shellCommandHandler, + shellController, + bubbleData, + surfaceSynchronizer, + FloatingContentCoordinator(), + mock<BubbleDataRepository>(), + mock<IStatusBarService>(), + windowManager, + WindowManagerShellWrapper(mainExecutor), + mock<UserManager>(), + mock<LauncherApps>(), + mock<BubbleLogger>(), + mock<TaskStackListenerImpl>(), + ShellTaskOrganizer(mainExecutor), + bubblePositioner, + mock<DisplayController>(), + null, + null, + mainExecutor, + mock<Handler>(), + mock<ShellExecutor>(), + mock<TaskViewTransitions>(), + mock<Transitions>(), + mock<SyncTransactionQueue>(), + mock<IWindowManager>(), + mock<BubbleProperties>()) + + bubbleStackView = BubbleStackView(context, bubbleController, bubbleData, + surfaceSynchronizer, FloatingContentCoordinator(), mainExecutor) + bubbleBarLayerView = BubbleBarLayerView(context, bubbleController) + } + + @Test + fun testPopulate() { + bubble = createBubbleWithShortcut() + val info = BubbleViewInfoTask.BubbleViewInfo.populate(context, + bubbleController, bubbleStackView, iconFactory, bubble, false /* skipInflation */) + assertThat(info!!).isNotNull() + + assertThat(info.imageView).isNotNull() + assertThat(info.expandedView).isNotNull() + assertThat(info.bubbleBarExpandedView).isNull() + + assertThat(info.shortcutInfo).isNotNull() + assertThat(info.appName).isNotEmpty() + assertThat(info.rawBadgeBitmap).isNotNull() + assertThat(info.dotPath).isNotNull() + assertThat(info.bubbleBitmap).isNotNull() + assertThat(info.badgeBitmap).isNotNull() + } + + @Test + fun testPopulateForBubbleBar() { + bubble = createBubbleWithShortcut() + val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(context, + bubbleController, bubbleBarLayerView, iconFactory, bubble, + false /* skipInflation */) + assertThat(info!!).isNotNull() + + assertThat(info.imageView).isNull() + assertThat(info.expandedView).isNull() + assertThat(info.bubbleBarExpandedView).isNotNull() + + assertThat(info.shortcutInfo).isNotNull() + assertThat(info.appName).isNotEmpty() + assertThat(info.rawBadgeBitmap).isNotNull() + assertThat(info.dotPath).isNotNull() + assertThat(info.bubbleBitmap).isNotNull() + assertThat(info.badgeBitmap).isNotNull() + } + + @Test + fun testPopulate_invalidShortcutIcon() { + bubble = createBubbleWithShortcut() + + // This eventually calls down to load the shortcut icon from the app, simulate an + // exception here if the app has an issue loading the shortcut icon; we default to + // the app icon in that case / none of the icons will be null. + val mockIconFactory = mock<BubbleIconFactory>() + whenever(mockIconFactory.getBubbleDrawable(eq(context), eq(bubble.shortcutInfo), + any())).doThrow(RuntimeException()) + + val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(context, + bubbleController, bubbleBarLayerView, iconFactory, bubble, + true /* skipInflation */) + assertThat(info).isNotNull() + + assertThat(info?.shortcutInfo).isNotNull() + assertThat(info?.appName).isNotEmpty() + assertThat(info?.rawBadgeBitmap).isNotNull() + assertThat(info?.dotPath).isNotNull() + assertThat(info?.bubbleBitmap).isNotNull() + assertThat(info?.badgeBitmap).isNotNull() + } + + private fun createBubbleWithShortcut(): Bubble { + val shortcutInfo = ShortcutInfo.Builder(mContext, "mockShortcutId").build() + return Bubble("mockKey", shortcutInfo, 1000, Resources.ID_NULL, + "mockTitle", 0 /* taskId */, "mockLocus", true /* isDismissible */, + mainExecutor, metadataFlagListener) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java new file mode 100644 index 000000000000..f8eb50b978a5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.bubbles; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.WindowManager.TRANSIT_CHANGE; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_FRONT; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.window.IWindowContainerToken; +import android.window.TransitionInfo; +import android.window.WindowContainerToken; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.transition.TransitionInfoBuilder; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests of {@link BubblesTransitionObserver}. + */ +@SmallTest +public class BubblesTransitionObserverTest { + + @Mock + private BubbleController mBubbleController; + @Mock + private BubbleData mBubbleData; + + @Mock + private IBinder mTransition; + @Mock + private SurfaceControl.Transaction mStartT; + @Mock + private SurfaceControl.Transaction mFinishT; + + @Mock + private Bubble mBubble; + + private BubblesTransitionObserver mTransitionObserver; + + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mTransitionObserver = new BubblesTransitionObserver(mBubbleController, mBubbleData); + } + + @Test + public void testOnTransitionReady_open_collapsesStack() { + when(mBubbleData.isExpanded()).thenReturn(true); + when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); + when(mBubble.getTaskId()).thenReturn(1); + when(mBubbleController.isStackAnimating()).thenReturn(false); + + TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2)); + + mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); + + verify(mBubbleData).setExpanded(eq(false)); + } + + @Test + public void testOnTransitionReady_toFront_collapsesStack() { + when(mBubbleData.isExpanded()).thenReturn(true); + when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); + when(mBubble.getTaskId()).thenReturn(1); + when(mBubbleController.isStackAnimating()).thenReturn(false); + + TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(2)); + + mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); + + verify(mBubbleData).setExpanded(eq(false)); + } + + @Test + public void testOnTransitionReady_noTaskInfo_skip() { + when(mBubbleData.isExpanded()).thenReturn(true); + when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); + when(mBubble.getTaskId()).thenReturn(1); + when(mBubbleController.isStackAnimating()).thenReturn(false); + + // Null task info + TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, null /* taskInfo */); + + mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); + + verify(mBubbleData, never()).setExpanded(eq(false)); + } + + @Test + public void testOnTransitionReady_noTaskId_skip() { + when(mBubbleData.isExpanded()).thenReturn(true); + when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); + when(mBubble.getTaskId()).thenReturn(1); + when(mBubbleController.isStackAnimating()).thenReturn(false); + + // Invalid task id + TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, + createTaskInfo(INVALID_TASK_ID)); + + mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); + + verify(mBubbleData, never()).setExpanded(eq(false)); + } + + @Test + public void testOnTransitionReady_notOpening_skip() { + when(mBubbleData.isExpanded()).thenReturn(true); + when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); + when(mBubble.getTaskId()).thenReturn(1); + when(mBubbleController.isStackAnimating()).thenReturn(false); + + // Transits that aren't opening + TransitionInfo info = createTransitionInfo(TRANSIT_CHANGE, createTaskInfo(2)); + mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); + + info = createTransitionInfo(TRANSIT_CLOSE, createTaskInfo(3)); + mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); + + info = createTransitionInfo(TRANSIT_TO_BACK, createTaskInfo(4)); + mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); + + verify(mBubbleData, never()).setExpanded(eq(false)); + } + + @Test + public void testOnTransitionReady_stackAnimating_skip() { + when(mBubbleData.isExpanded()).thenReturn(true); + when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); + when(mBubble.getTaskId()).thenReturn(1); + when(mBubbleController.isStackAnimating()).thenReturn(true); // Stack is animating + + TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2)); + + mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); + + verify(mBubbleData, never()).setExpanded(eq(false)); + } + + @Test + public void testOnTransitionReady_stackNotExpanded_skip() { + when(mBubbleData.isExpanded()).thenReturn(false); // Stack is not expanded + when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); + when(mBubble.getTaskId()).thenReturn(1); + when(mBubbleController.isStackAnimating()).thenReturn(false); + + TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(2)); + + mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); + + verify(mBubbleData, never()).setExpanded(eq(false)); + } + + @Test + public void testOnTransitionReady_noSelectedBubble_skip() { + when(mBubbleData.isExpanded()).thenReturn(true); + when(mBubbleData.getSelectedBubble()).thenReturn(null); // No selected bubble + when(mBubble.getTaskId()).thenReturn(1); + when(mBubbleController.isStackAnimating()).thenReturn(false); + + TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2)); + + mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); + + verify(mBubbleData, never()).setExpanded(eq(false)); + } + + @Test + public void testOnTransitionReady_openingMatchesExpanded_skip() { + when(mBubbleData.isExpanded()).thenReturn(true); + when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); + when(mBubble.getTaskId()).thenReturn(1); + when(mBubbleController.isStackAnimating()).thenReturn(false); + + // What's moving to front is same as the opened bubble + TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(1)); + + mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); + + verify(mBubbleData, never()).setExpanded(eq(false)); + } + + private ActivityManager.RunningTaskInfo createTaskInfo(int taskId) { + final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); + taskInfo.taskId = taskId; + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + return taskInfo; + } + + private TransitionInfo createTransitionInfo(int changeType, + ActivityManager.RunningTaskInfo info) { + final TransitionInfo.Change change = new TransitionInfo.Change( + new WindowContainerToken(mock(IWindowContainerToken.class)), + mock(SurfaceControl.class)); + change.setMode(changeType); + change.setTaskInfo(info); + + return new TransitionInfoBuilder(TRANSIT_OPEN, 0) + .addChange(change).build(); + } + +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt index 9f0d89bc3128..52375850b9a5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt @@ -27,15 +27,13 @@ import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertEquals import org.junit.Before import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.anyString -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mockito -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.reset -import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.reset +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions @SmallTest @RunWith(AndroidTestingRunner::class) @@ -66,7 +64,7 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { @Before fun setup() { - launcherApps = mock(LauncherApps::class.java) + launcherApps = mock<LauncherApps>() repository = BubbleVolatileRepository(launcherApps) } @@ -98,7 +96,7 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { repository.addBubbles(user11.identifier, listOf(bubble12)) assertEquals(listOf(bubble11, bubble12), repository.getEntities(user11.identifier)) - Mockito.verifyNoMoreInteractions(launcherApps) + verifyNoMoreInteractions(launcherApps) } @Test @@ -167,9 +165,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { assertThat(ret).isTrue() // bubbles were removed assertThat(repository.getEntities(user0.identifier).toList()).isEmpty() - verify(launcherApps, never()).uncacheShortcuts(anyString(), - any(), - any(UserHandle::class.java), anyInt()) + verify(launcherApps, never()) + .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>()) } @Test @@ -184,9 +181,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { assertThat(repository.getEntities(user0.identifier).toList()) .isEqualTo(listOf(bubble1, bubble3)) - verify(launcherApps, never()).uncacheShortcuts(anyString(), - any(), - any(UserHandle::class.java), anyInt()) + verify(launcherApps, never()) + .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>()) } @Test @@ -200,9 +196,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { assertThat(repository.getEntities(user0.identifier).toList()) .isEqualTo(listOf(bubble1, bubble2, bubble3)) - verify(launcherApps, never()).uncacheShortcuts(anyString(), - any(), - any(UserHandle::class.java), anyInt()) + verify(launcherApps, never()) + .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>()) } @Test @@ -219,9 +214,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { user11.identifier)) assertThat(ret).isFalse() // bubbles were NOT removed - verify(launcherApps, never()).uncacheShortcuts(anyString(), - any(), - any(UserHandle::class.java), anyInt()) + verify(launcherApps, never()) + .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>()) } @Test @@ -237,9 +231,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { assertThat(ret).isTrue() // bubbles were removed assertThat(repository.getEntities(user0.identifier).toList()).isEmpty() - verify(launcherApps, never()).uncacheShortcuts(anyString(), - any(), - any(UserHandle::class.java), anyInt()) + verify(launcherApps, never()) + .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>()) // User 11 bubbles should still be here assertThat(repository.getEntities(user11.identifier).toList()) @@ -261,9 +254,8 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { // bubble2 is the work profile bubble and should be removed assertThat(repository.getEntities(user0.identifier).toList()) .isEqualTo(listOf(bubble1, bubble3)) - verify(launcherApps, never()).uncacheShortcuts(anyString(), - any(), - any(UserHandle::class.java), anyInt()) + verify(launcherApps, never()) + .uncacheShortcuts(any<String>(), any(), any<UserHandle>(), any<Int>()) // User 11 bubbles should still be here assertThat(repository.getEntities(user11.identifier).toList()) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index fe2da5dd19fc..56d0f8e13f08 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -19,6 +19,10 @@ package com.android.wm.shell.common.split; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -130,7 +134,7 @@ public class SplitLayoutTests extends ShellTestCase { @Test public void testSetDivideRatio() { mSplitLayout.setDividePosition(200, false /* applyLayoutChange */); - mSplitLayout.setDivideRatio(0.5f); + mSplitLayout.setDivideRatio(SNAP_TO_50_50); assertThat(mSplitLayout.getDividePosition()).isEqualTo( mSplitLayout.mDividerSnapAlgorithm.getMiddleTarget().position); } @@ -146,7 +150,7 @@ public class SplitLayoutTests extends ShellTestCase { public void testSnapToDismissStart() { // verify it callbacks properly when the snap target indicates dismissing split. DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */, - DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START); + SNAP_TO_START_AND_DISMISS); mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget); waitDividerFlingFinished(); @@ -158,7 +162,7 @@ public class SplitLayoutTests extends ShellTestCase { public void testSnapToDismissEnd() { // verify it callbacks properly when the snap target indicates dismissing split. DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */, - DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END); + SNAP_TO_END_AND_DISMISS); mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget); waitDividerFlingFinished(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenConstantsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenConstantsTest.kt new file mode 100644 index 000000000000..fe261107d65b --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenConstantsTest.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common.split + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SplitScreenConstantsTest { + + /** + * Ensures that some important constants are not changed from their set values. These values + * are persisted in user-defined app pairs, and changing them will break things. + */ + @Test + fun shouldKeepExistingConstantValues() { + assertEquals( + "the value of SPLIT_POSITION_TOP_OR_LEFT should be 0", + 0, + SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT, + ) + assertEquals( + "the value of SPLIT_POSITION_BOTTOM_OR_RIGHT should be 1", + 1, + SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT, + ) + assertEquals( + "the value of SNAP_TO_30_70 should be 0", + 0, + SplitScreenConstants.SNAP_TO_30_70, + ) + assertEquals( + "the value of SNAP_TO_50_50 should be 1", + 1, + SplitScreenConstants.SNAP_TO_50_50, + ) + assertEquals( + "the value of SNAP_TO_70_30 should be 2", + 2, + SplitScreenConstants.SNAP_TO_70_30, + ) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java deleted file mode 100644 index 605a762a395f..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.desktopmode; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS; -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_CLOSE; -import static android.view.WindowManager.TRANSIT_NONE; -import static android.view.WindowManager.TRANSIT_OPEN; -import static android.view.WindowManager.TRANSIT_TO_FRONT; -import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask; -import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask; -import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createHomeTask; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -import android.app.ActivityManager.RunningTaskInfo; -import android.os.Binder; -import android.os.Handler; -import android.os.IBinder; -import android.testing.AndroidTestingRunner; -import android.window.DisplayAreaInfo; -import android.window.TransitionRequestInfo; -import android.window.WindowContainerTransaction; -import android.window.WindowContainerTransaction.Change; -import android.window.WindowContainerTransaction.HierarchyOp; - -import androidx.test.filters.SmallTest; - -import com.android.dx.mockito.inline.extended.StaticMockitoSession; -import com.android.wm.shell.MockToken; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.TestShellExecutor; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.sysui.ShellController; -import com.android.wm.shell.sysui.ShellInit; -import com.android.wm.shell.transition.Transitions; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; - -import java.util.ArrayList; -import java.util.Arrays; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class DesktopModeControllerTest extends ShellTestCase { - - private static final int SECOND_DISPLAY = 2; - - @Mock - private ShellController mShellController; - @Mock - private ShellTaskOrganizer mShellTaskOrganizer; - @Mock - private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; - @Mock - private ShellExecutor mTestExecutor; - @Mock - private Handler mMockHandler; - @Mock - private Transitions mTransitions; - private DesktopModeController mController; - private DesktopModeTaskRepository mDesktopModeTaskRepository; - private ShellInit mShellInit; - private StaticMockitoSession mMockitoSession; - - @Before - public void setUp() { - mMockitoSession = mockitoSession().mockStatic(DesktopModeStatus.class).startMocking(); - when(DesktopModeStatus.isProto1Enabled()).thenReturn(true); - when(DesktopModeStatus.isActive(any())).thenReturn(true); - - mShellInit = Mockito.spy(new ShellInit(mTestExecutor)); - - mDesktopModeTaskRepository = new DesktopModeTaskRepository(); - - mController = createController(); - - when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>()); - - mShellInit.init(); - clearInvocations(mShellTaskOrganizer); - clearInvocations(mRootTaskDisplayAreaOrganizer); - clearInvocations(mTransitions); - } - - @After - public void tearDown() { - mMockitoSession.finishMocking(); - } - - @Test - public void instantiate_addInitCallback() { - verify(mShellInit).addInitCallback(any(), any()); - } - - @Test - public void instantiate_flagOff_doNotAddInitCallback() { - when(DesktopModeStatus.isProto1Enabled()).thenReturn(false); - clearInvocations(mShellInit); - - createController(); - - verify(mShellInit, never()).addInitCallback(any(), any()); - } - - @Test - public void testDesktopModeEnabled_rootTdaSetToFreeform() { - DisplayAreaInfo displayAreaInfo = createMockDisplayArea(); - - mController.updateDesktopModeActive(true); - WindowContainerTransaction wct = getDesktopModeSwitchTransaction(); - - // 1 change: Root TDA windowing mode - assertThat(wct.getChanges().size()).isEqualTo(1); - // Verify WCT has a change for setting windowing mode to freeform - Change change = wct.getChanges().get(displayAreaInfo.token.asBinder()); - assertThat(change).isNotNull(); - assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM); - } - - @Test - public void testDesktopModeDisabled_rootTdaSetToFullscreen() { - DisplayAreaInfo displayAreaInfo = createMockDisplayArea(); - - mController.updateDesktopModeActive(false); - WindowContainerTransaction wct = getDesktopModeSwitchTransaction(); - - // 1 change: Root TDA windowing mode - assertThat(wct.getChanges().size()).isEqualTo(1); - // Verify WCT has a change for setting windowing mode to fullscreen - Change change = wct.getChanges().get(displayAreaInfo.token.asBinder()); - assertThat(change).isNotNull(); - assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN); - } - - @Test - public void testDesktopModeEnabled_windowingModeCleared() { - createMockDisplayArea(); - RunningTaskInfo freeformTask = createFreeformTask(); - RunningTaskInfo fullscreenTask = createFullscreenTask(); - RunningTaskInfo homeTask = createHomeTask(); - when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>( - Arrays.asList(freeformTask, fullscreenTask, homeTask))); - - mController.updateDesktopModeActive(true); - WindowContainerTransaction wct = getDesktopModeSwitchTransaction(); - - // 2 changes: Root TDA windowing mode and 1 task - assertThat(wct.getChanges().size()).isEqualTo(2); - // No changes for tasks that are not standard or freeform - assertThat(wct.getChanges().get(fullscreenTask.token.asBinder())).isNull(); - assertThat(wct.getChanges().get(homeTask.token.asBinder())).isNull(); - // Standard freeform task has windowing mode cleared - Change change = wct.getChanges().get(freeformTask.token.asBinder()); - assertThat(change).isNotNull(); - assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); - } - - @Test - public void testDesktopModeDisabled_windowingModeAndBoundsCleared() { - createMockDisplayArea(); - RunningTaskInfo freeformTask = createFreeformTask(); - RunningTaskInfo fullscreenTask = createFullscreenTask(); - RunningTaskInfo homeTask = createHomeTask(); - when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>( - Arrays.asList(freeformTask, fullscreenTask, homeTask))); - - mController.updateDesktopModeActive(false); - WindowContainerTransaction wct = getDesktopModeSwitchTransaction(); - - // 3 changes: Root TDA windowing mode and 2 tasks - assertThat(wct.getChanges().size()).isEqualTo(3); - // No changes to home task - assertThat(wct.getChanges().get(homeTask.token.asBinder())).isNull(); - // Standard tasks have bounds cleared - assertThatBoundsCleared(wct.getChanges().get(freeformTask.token.asBinder())); - assertThatBoundsCleared(wct.getChanges().get(fullscreenTask.token.asBinder())); - // Freeform standard tasks have windowing mode cleared - assertThat(wct.getChanges().get( - freeformTask.token.asBinder()).getWindowingMode()).isEqualTo( - WINDOWING_MODE_UNDEFINED); - } - - @Test - public void testDesktopModeEnabled_homeTaskBehindVisibleTask() { - createMockDisplayArea(); - RunningTaskInfo fullscreenTask1 = createFullscreenTask(); - fullscreenTask1.isVisible = true; - RunningTaskInfo fullscreenTask2 = createFullscreenTask(); - fullscreenTask2.isVisible = false; - RunningTaskInfo homeTask = createHomeTask(); - when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>( - Arrays.asList(fullscreenTask1, fullscreenTask2, homeTask))); - - mController.updateDesktopModeActive(true); - WindowContainerTransaction wct = getDesktopModeSwitchTransaction(); - - // Check that there are hierarchy changes for home task and visible task - assertThat(wct.getHierarchyOps()).hasSize(2); - // First show home task - HierarchyOp op1 = wct.getHierarchyOps().get(0); - assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op1.getContainer()).isEqualTo(homeTask.token.asBinder()); - - // Then visible task on top of it - HierarchyOp op2 = wct.getHierarchyOps().get(1); - assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op2.getContainer()).isEqualTo(fullscreenTask1.token.asBinder()); - } - - @Test - public void testShowDesktopApps_allAppsInvisible_bringsToFront() { - // Set up two active tasks on desktop, task2 is on top of task1. - RunningTaskInfo freeformTask1 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask1.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask1.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks( - DEFAULT_DISPLAY, freeformTask1.taskId, false /* visible */); - RunningTaskInfo freeformTask2 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask2.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask2.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks( - DEFAULT_DISPLAY, freeformTask2.taskId, false /* visible */); - when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn( - freeformTask1); - when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn( - freeformTask2); - - // Run show desktop apps logic - mController.showDesktopApps(DEFAULT_DISPLAY); - - final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); - // Check wct has reorder calls - assertThat(wct.getHierarchyOps()).hasSize(2); - - // Task 1 appeared first, must be first reorder to top. - HierarchyOp op1 = wct.getHierarchyOps().get(0); - assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op1.getContainer()).isEqualTo(freeformTask1.token.asBinder()); - - // Task 2 appeared last, must be last reorder to top. - HierarchyOp op2 = wct.getHierarchyOps().get(1); - assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op2.getContainer()).isEqualTo(freeformTask2.token.asBinder()); - } - - @Test - public void testShowDesktopApps_appsAlreadyVisible_bringsToFront() { - final RunningTaskInfo task1 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId, - true /* visible */); - when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1); - final RunningTaskInfo task2 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId, - true /* visible */); - when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2); - - mController.showDesktopApps(DEFAULT_DISPLAY); - - final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); - // Check wct has reorder calls - assertThat(wct.getHierarchyOps()).hasSize(2); - // Task 1 appeared first, must be first reorder to top. - HierarchyOp op1 = wct.getHierarchyOps().get(0); - assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op1.getContainer()).isEqualTo(task1.token.asBinder()); - - // Task 2 appeared last, must be last reorder to top. - HierarchyOp op2 = wct.getHierarchyOps().get(1); - assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op2.getContainer()).isEqualTo(task2.token.asBinder()); - } - - @Test - public void testShowDesktopApps_someAppsInvisible_reordersAll() { - final RunningTaskInfo task1 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId, - false /* visible */); - when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1); - final RunningTaskInfo task2 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId, - true /* visible */); - when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2); - - mController.showDesktopApps(DEFAULT_DISPLAY); - - final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); - // Both tasks should be reordered to top, even if one was already visible. - assertThat(wct.getHierarchyOps()).hasSize(2); - final HierarchyOp op1 = wct.getHierarchyOps().get(0); - assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op1.getContainer()).isEqualTo(task1.token.asBinder()); - final HierarchyOp op2 = wct.getHierarchyOps().get(1); - assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op2.getContainer()).isEqualTo(task2.token.asBinder()); - } - - @Test - public void testShowDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() { - RunningTaskInfo taskDefaultDisplay = createFreeformTask(DEFAULT_DISPLAY); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks( - DEFAULT_DISPLAY, taskDefaultDisplay.taskId, false /* visible */); - when(mShellTaskOrganizer.getRunningTaskInfo(taskDefaultDisplay.taskId)).thenReturn( - taskDefaultDisplay); - - RunningTaskInfo taskSecondDisplay = createFreeformTask(SECOND_DISPLAY); - mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks( - SECOND_DISPLAY, taskSecondDisplay.taskId, false /* visible */); - when(mShellTaskOrganizer.getRunningTaskInfo(taskSecondDisplay.taskId)).thenReturn( - taskSecondDisplay); - - mController.showDesktopApps(DEFAULT_DISPLAY); - - WindowContainerTransaction wct = getBringAppsToFrontTransaction(); - assertThat(wct.getHierarchyOps()).hasSize(1); - HierarchyOp op = wct.getHierarchyOps().get(0); - assertThat(op.getContainer()).isEqualTo(taskDefaultDisplay.token.asBinder()); - } - - @Test - public void testGetVisibleTaskCount_noTasks_returnsZero() { - assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0); - } - - @Test - public void testGetVisibleTaskCount_twoTasks_bothVisible_returnsTwo() { - RunningTaskInfo task1 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId, - true /* visible */); - - RunningTaskInfo task2 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId, - true /* visible */); - - assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2); - } - - @Test - public void testGetVisibleTaskCount_twoTasks_oneVisible_returnsOne() { - RunningTaskInfo task1 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId, - true /* visible */); - - RunningTaskInfo task2 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId, - false /* visible */); - - assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1); - } - - @Test - public void testGetVisibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() { - RunningTaskInfo taskDefaultDisplay = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, - taskDefaultDisplay.taskId, - true /* visible */); - - RunningTaskInfo taskSecondDisplay = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId); - mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(SECOND_DISPLAY, - taskSecondDisplay.taskId, - true /* visible */); - - assertThat(mController.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1); - } - - @Test - public void testHandleTransitionRequest_desktopModeNotActive_returnsNull() { - when(DesktopModeStatus.isActive(any())).thenReturn(false); - WindowContainerTransaction wct = mController.handleRequest( - new Binder(), - new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); - assertThat(wct).isNull(); - } - - @Test - public void testHandleTransitionRequest_unsupportedTransit_returnsNull() { - WindowContainerTransaction wct = mController.handleRequest( - new Binder(), - new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); - assertThat(wct).isNull(); - } - - @Test - public void testHandleTransitionRequest_notFreeform_returnsNull() { - RunningTaskInfo trigger = new RunningTaskInfo(); - trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - WindowContainerTransaction wct = mController.handleRequest( - new Binder(), - new TransitionRequestInfo(TRANSIT_TO_FRONT, trigger, null /* remote */)); - assertThat(wct).isNull(); - } - - @Test - public void testHandleTransitionRequest_taskOpen_returnsWct() { - RunningTaskInfo trigger = new RunningTaskInfo(); - trigger.token = new MockToken().token(); - trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); - WindowContainerTransaction wct = mController.handleRequest( - mock(IBinder.class), - new TransitionRequestInfo(TRANSIT_OPEN, trigger, null /* remote */)); - assertThat(wct).isNotNull(); - } - - @Test - public void testHandleTransitionRequest_taskToFront_returnsWct() { - RunningTaskInfo trigger = new RunningTaskInfo(); - trigger.token = new MockToken().token(); - trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); - WindowContainerTransaction wct = mController.handleRequest( - mock(IBinder.class), - new TransitionRequestInfo(TRANSIT_TO_FRONT, trigger, null /* remote */)); - assertThat(wct).isNotNull(); - } - - @Test - public void testHandleTransitionRequest_taskOpen_doesNotStartAnotherTransition() { - RunningTaskInfo trigger = new RunningTaskInfo(); - trigger.token = new MockToken().token(); - trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); - mController.handleRequest( - mock(IBinder.class), - new TransitionRequestInfo(TRANSIT_OPEN, trigger, null /* remote */)); - verifyZeroInteractions(mTransitions); - } - - private DesktopModeController createController() { - return new DesktopModeController(mContext, mShellInit, mShellController, - mShellTaskOrganizer, mRootTaskDisplayAreaOrganizer, mTransitions, - mDesktopModeTaskRepository, mMockHandler, new TestShellExecutor()); - } - - private DisplayAreaInfo createMockDisplayArea() { - DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(new MockToken().token(), - mContext.getDisplayId(), 0); - when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId())) - .thenReturn(displayAreaInfo); - return displayAreaInfo; - } - - private WindowContainerTransaction getDesktopModeSwitchTransaction() { - ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass( - WindowContainerTransaction.class); - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - verify(mTransitions).startTransition(eq(TRANSIT_CHANGE), arg.capture(), any()); - } else { - verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture()); - } - return arg.getValue(); - } - - private WindowContainerTransaction getBringAppsToFrontTransaction() { - final ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass( - WindowContainerTransaction.class); - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - verify(mTransitions).startTransition(eq(TRANSIT_NONE), arg.capture(), any()); - } else { - verify(mShellTaskOrganizer).applyTransaction(arg.capture()); - } - return arg.getValue(); - } - - private void assertThatBoundsCleared(Change change) { - assertThat((change.getWindowSetMask() & WINDOW_CONFIG_BOUNDS) != 0).isTrue(); - assertThat(change.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue(); - } - -} 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 5d87cf8b25a6..ebcb6407a6fd 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 @@ -28,10 +28,10 @@ import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY import android.view.WindowManager import android.view.WindowManager.TRANSIT_CHANGE -import android.view.WindowManager.TRANSIT_NONE import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_FRONT import android.window.DisplayAreaInfo +import android.window.RemoteTransition import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER @@ -43,6 +43,7 @@ import com.android.wm.shell.MockToken import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.transition.TestRemoteTransition import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.TestShellExecutor import com.android.wm.shell.common.DisplayController @@ -52,11 +53,17 @@ import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener +import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS +import com.android.wm.shell.transition.Transitions.TransitionHandler import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage @@ -67,6 +74,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.eq +import org.mockito.ArgumentMatchers.isA import org.mockito.ArgumentMatchers.isNull import org.mockito.Mock import org.mockito.Mockito @@ -94,11 +102,14 @@ class DesktopTasksControllerTest : ShellTestCase() { ToggleResizeDesktopTaskTransitionHandler @Mock lateinit var launchAdjacentController: LaunchAdjacentController @Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration + @Mock lateinit var splitScreenController: SplitScreenController + @Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler private lateinit var mockitoSession: StaticMockitoSession private lateinit var controller: DesktopTasksController private lateinit var shellInit: ShellInit private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository + private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener private val shellExecutor = TestShellExecutor() // Mock running tasks are registered here so we can get the list from mock shell task organizer @@ -107,7 +118,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Before fun setUp() { mockitoSession = mockitoSession().mockStatic(DesktopModeStatus::class.java).startMocking() - whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(true) + whenever(DesktopModeStatus.isEnabled()).thenReturn(true) shellInit = Mockito.spy(ShellInit(testExecutor)) desktopModeTaskRepository = DesktopModeTaskRepository() @@ -116,8 +127,13 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() } controller = createController() + controller.splitScreenController = splitScreenController shellInit.init() + + val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java) + verify(recentsTransitionHandler).addTransitionStateListener(captor.capture()) + recentsTransitionStateListener = captor.value } private fun createController(): DesktopTasksController { @@ -136,6 +152,7 @@ class DesktopTasksControllerTest : ShellTestCase() { mToggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository, launchAdjacentController, + recentsTransitionHandler, shellExecutor ) } @@ -154,7 +171,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun instantiate_flagOff_doNotAddInitCallback() { - whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(false) + whenever(DesktopModeStatus.isEnabled()).thenReturn(false) clearInvocations(shellInit) createController() @@ -170,9 +187,10 @@ class DesktopTasksControllerTest : ShellTestCase() { markTaskHidden(task1) markTaskHidden(task2) - controller.showDesktopApps(DEFAULT_DISPLAY) + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) - val wct = getLatestWct(expectTransition = TRANSIT_NONE) + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) assertThat(wct.hierarchyOps).hasSize(3) // Expect order to be from bottom: home, task1, task2 wct.assertReorderAt(index = 0, homeTask) @@ -188,9 +206,10 @@ class DesktopTasksControllerTest : ShellTestCase() { markTaskVisible(task1) markTaskVisible(task2) - controller.showDesktopApps(DEFAULT_DISPLAY) + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) - val wct = getLatestWct(expectTransition = TRANSIT_NONE) + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) assertThat(wct.hierarchyOps).hasSize(3) // Expect order to be from bottom: home, task1, task2 wct.assertReorderAt(index = 0, homeTask) @@ -206,9 +225,10 @@ class DesktopTasksControllerTest : ShellTestCase() { markTaskHidden(task1) markTaskVisible(task2) - controller.showDesktopApps(DEFAULT_DISPLAY) + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) - val wct = getLatestWct(expectTransition = TRANSIT_NONE) + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) assertThat(wct.hierarchyOps).hasSize(3) // Expect order to be from bottom: home, task1, task2 wct.assertReorderAt(index = 0, homeTask) @@ -220,9 +240,10 @@ class DesktopTasksControllerTest : ShellTestCase() { fun showDesktopApps_noActiveTasks_reorderHomeToTop() { val homeTask = setUpHomeTask() - controller.showDesktopApps(DEFAULT_DISPLAY) + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) - val wct = getLatestWct(expectTransition = TRANSIT_NONE) + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) assertThat(wct.hierarchyOps).hasSize(1) wct.assertReorderAt(index = 0, homeTask) } @@ -236,9 +257,10 @@ class DesktopTasksControllerTest : ShellTestCase() { markTaskHidden(taskDefaultDisplay) markTaskHidden(taskSecondDisplay) - controller.showDesktopApps(DEFAULT_DISPLAY) + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) - val wct = getLatestWct(expectTransition = TRANSIT_NONE) + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) assertThat(wct.hierarchyOps).hasSize(2) // Expect order to be from bottom: home, task wct.assertReorderAt(index = 0, homeTaskDefaultDisplay) @@ -341,11 +363,35 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun moveToDesktop_splitTaskExitsSplit() { + val task = setUpSplitScreenTask() + controller.moveToDesktop(desktopModeWindowDecoration, task) + val wct = getLatestMoveToDesktopWct() + assertThat(wct.changes[task.token.asBinder()]?.windowingMode) + .isEqualTo(WINDOWING_MODE_FREEFORM) + verify(splitScreenController).prepareExitSplitScreen(any(), anyInt(), + eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP) + ) + } + + @Test + fun moveToDesktop_fullscreenTaskDoesNotExitSplit() { + val task = setUpFullscreenTask() + controller.moveToDesktop(desktopModeWindowDecoration, task) + val wct = getLatestMoveToDesktopWct() + assertThat(wct.changes[task.token.asBinder()]?.windowingMode) + .isEqualTo(WINDOWING_MODE_FREEFORM) + verify(splitScreenController, never()).prepareExitSplitScreen(any(), anyInt(), + eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP) + ) + } + + @Test fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() { val task = setUpFreeformTask() task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN controller.moveToFullscreen(task) - val wct = getLatestWct(expectTransition = TRANSIT_CHANGE) + val wct = getLatestWct(type = TRANSIT_CHANGE) assertThat(wct.changes[task.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) } @@ -355,7 +401,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask() task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM controller.moveToFullscreen(task) - val wct = getLatestWct(expectTransition = TRANSIT_CHANGE) + val wct = getLatestWct(type = TRANSIT_CHANGE) assertThat(wct.changes[task.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FULLSCREEN) } @@ -373,7 +419,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.moveToFullscreen(taskDefaultDisplay) - with(getLatestWct(expectTransition = TRANSIT_CHANGE)) { + with(getLatestWct(type = TRANSIT_CHANGE)) { assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder()) assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder()) } @@ -386,7 +432,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.moveTaskToFront(task1) - val wct = getLatestWct(expectTransition = TRANSIT_TO_FRONT) + val wct = getLatestWct(type = TRANSIT_TO_FRONT) assertThat(wct.hierarchyOps).hasSize(1) wct.assertReorderAt(index = 0, task1) } @@ -411,7 +457,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY) controller.moveToNextDisplay(task.taskId) - with(getLatestWct(expectTransition = TRANSIT_CHANGE)) { + with(getLatestWct(type = TRANSIT_CHANGE)) { assertThat(hierarchyOps).hasSize(1) assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder()) assertThat(hierarchyOps[0].isReparent).isTrue() @@ -433,7 +479,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(displayId = SECOND_DISPLAY) controller.moveToNextDisplay(task.taskId) - with(getLatestWct(expectTransition = TRANSIT_CHANGE)) { + with(getLatestWct(type = TRANSIT_CHANGE)) { assertThat(hierarchyOps).hasSize(1) assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder()) assertThat(hierarchyOps[0].isReparent).isTrue() @@ -629,6 +675,20 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun handleRequest_recentsAnimationRunning_returnNull() { + // Set up a visible freeform task so a fullscreen task should be converted to freeform + val freeformTask = setUpFreeformTask() + markTaskVisible(freeformTask) + + // Mark recents animation running + recentsTransitionStateListener.onAnimationStateChanged(true) + + // Open a fullscreen task, check that it does not result in a WCT with changes to it + val fullscreenTask = createFullscreenTask() + assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull() + } + + @Test fun stashDesktopApps_stateUpdates() { whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true) @@ -695,6 +755,13 @@ class DesktopTasksControllerTest : ShellTestCase() { return task } + private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { + val task = createSplitScreenTask(displayId) + whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + runningTasks.add(task) + return task + } + private fun markTaskVisible(task: RunningTaskInfo) { desktopModeTaskRepository.updateVisibleFreeformTasks( task.displayId, @@ -712,11 +779,16 @@ class DesktopTasksControllerTest : ShellTestCase() { } private fun getLatestWct( - @WindowManager.TransitionType expectTransition: Int = TRANSIT_OPEN + @WindowManager.TransitionType type: Int = TRANSIT_OPEN, + handlerClass: Class<out TransitionHandler>? = null ): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) if (ENABLE_SHELL_TRANSITIONS) { - verify(transitions).startTransition(eq(expectTransition), arg.capture(), isNull()) + if (handlerClass == null) { + verify(transitions).startTransition(eq(type), arg.capture(), isNull()) + } else { + verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass)) + } } else { verify(shellTaskOrganizer).applyTransaction(arg.capture()) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt index 29a757c19d98..2f6f3207137d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt @@ -21,6 +21,7 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW import android.view.Display.DEFAULT_DISPLAY import com.android.wm.shell.MockToken import com.android.wm.shell.TestRunningTaskInfoBuilder @@ -45,12 +46,25 @@ class DesktopTestHelpers { @JvmOverloads fun createFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { return TestRunningTaskInfoBuilder() - .setDisplayId(displayId) - .setToken(MockToken().token()) - .setActivityType(ACTIVITY_TYPE_STANDARD) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .setLastActiveTime(100) - .build() + .setDisplayId(displayId) + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .setLastActiveTime(100) + .build() + } + + /** Create a task that has windowing mode set to [WINDOWING_MODE_MULTI_WINDOW] */ + @JvmStatic + @JvmOverloads + fun createSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { + return TestRunningTaskInfoBuilder() + .setDisplayId(displayId) + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW) + .setLastActiveTime(100) + .build() } /** Create a new home task */ 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 69f664a3a89d..499e339bc682 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 @@ -37,8 +37,8 @@ import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; -import com.android.wm.shell.TransitionInfoBuilder; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.TransitionInfoBuilder; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.WindowDecorViewModel; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java index d34e27b57071..db98abbbcbf1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.content.ComponentName; +import android.graphics.Point; import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -60,6 +61,9 @@ public class PipBoundsStateTest extends ShellTestCase { /** The minimum possible size of the override min size's width or height */ private static final int OVERRIDABLE_MIN_SIZE = 40; + /** The margin of error for floating point results. */ + private static final float MARGIN_OF_ERROR = 0.05f; + private PipBoundsState mPipBoundsState; private SizeSpecSource mSizeSpecSource; private ComponentName mTestComponentName1; @@ -88,6 +92,27 @@ public class PipBoundsStateTest extends ShellTestCase { } @Test + public void testBoundsScale() { + mPipBoundsState.setMaxSize(300, 300); + mPipBoundsState.setBounds(new Rect(0, 0, 100, 100)); + + final int currentWidth = mPipBoundsState.getBounds().width(); + final Point maxSize = mPipBoundsState.getMaxSize(); + final float expectedBoundsScale = Math.min((float) currentWidth / maxSize.x, 1.0f); + + // test for currentWidth < maxWidth + assertEquals(expectedBoundsScale, mPipBoundsState.getBoundsScale(), MARGIN_OF_ERROR); + + // reset the bounds to be at the maximum size spec + mPipBoundsState.setBounds(new Rect(0, 0, maxSize.x, maxSize.y)); + assertEquals(1.0f, mPipBoundsState.getBoundsScale(), /* delta */ 0f); + + // reset the bounds to be over the maximum size spec + mPipBoundsState.setBounds(new Rect(0, 0, maxSize.x * 2, maxSize.y * 2)); + assertEquals(1.0f, mPipBoundsState.getBoundsScale(), /* delta */ 0f); + } + + @Test public void testSetReentryState() { final Size size = new Size(100, 100); final float snapFraction = 0.5f; 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 6777a5bd8ceb..9719ba89b4bb 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 @@ -28,11 +28,13 @@ import static org.mockito.Mockito.verify; import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.testing.TestableResources; import android.view.MotionEvent; import android.view.ViewConfiguration; import androidx.test.filters.SmallTest; +import com.android.wm.shell.R; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; @@ -98,6 +100,9 @@ public class PipResizeGestureHandlerTest extends ShellTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + final TestableResources res = mContext.getOrCreateTestableResources(); + res.addOverride(R.bool.config_pipEnablePinchResize, true); + mPipDisplayLayoutState = new PipDisplayLayoutState(mContext); mSizeSpecSource = new PhoneSizeSpecSource(mContext, mPipDisplayLayoutState); mPipBoundsState = new PipBoundsState(mContext, mSizeSpecSource, mPipDisplayLayoutState); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java index c40cd4069cab..45f6c8c7f69f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java @@ -23,7 +23,9 @@ import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_EXPAND_COLLAPSE; import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_FULLSCREEN; import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_MOVE; -import static org.junit.Assert.assertTrue; +import static java.util.Collections.EMPTY_LIST; + +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -34,7 +36,6 @@ import android.graphics.drawable.Icon; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.util.Log; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.pip.PipMediaController; @@ -46,7 +47,9 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; /** * Unit tests for {@link TvPipActionsProvider} @@ -69,35 +72,38 @@ public class TvPipActionProviderTest extends ShellTestCase { @Mock private PendingIntent mMockPendingIntent; - private RemoteAction createRemoteAction(int identifier) { + private int mNumberOfRemoteActionsCreated = 0; + + private RemoteAction createRemoteAction() { + final int identifier = mNumberOfRemoteActionsCreated++; return new RemoteAction(mMockIcon, "" + identifier, "" + identifier, mMockPendingIntent); } private List<RemoteAction> createRemoteActions(int numberOfActions) { List<RemoteAction> actions = new ArrayList<>(); for (int i = 0; i < numberOfActions; i++) { - actions.add(createRemoteAction(i)); + actions.add(createRemoteAction()); } return actions; } - private boolean checkActionsMatch(List<TvPipAction> actions, int[] actionTypes) { - for (int i = 0; i < actions.size(); i++) { - int type = actions.get(i).getActionType(); - if (type != actionTypes[i]) { - Log.e(TAG, "Action at index " + i + ": found " + type - + ", expected " + actionTypes[i]); - return false; - } - } - return true; + private void assertActionTypes(List<Integer> expected, List<Integer> actual) { + assertEquals(getActionTypesStrings(expected), getActionTypesStrings(actual)); + } + + private static List<String> getActionTypesStrings(List<Integer> actionTypes) { + return actionTypes.stream().map(a -> TvPipAction.getActionTypeString(a)) + .collect(Collectors.toList()); + } + + private List<Integer> getActionsTypes() { + return mActionsProvider.getActionsList().stream().map(a -> a.getActionType()) + .collect(Collectors.toList()); } @Before public void setUp() { - if (!isTelevision()) { - return; - } + assumeTelevision(); MockitoAnnotations.initMocks(this); mActionsProvider = new TvPipActionsProvider(mContext, mMockPipMediaController, mMockSystemActionsHandler); @@ -105,57 +111,51 @@ public class TvPipActionProviderTest extends ShellTestCase { @Test public void defaultSystemActions_regularPip() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE})); + assertActionTypes(Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE), + getActionsTypes()); } @Test public void defaultSystemActions_expandedPip() { - assumeTelevision(); mActionsProvider.updateExpansionEnabled(true); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE), + getActionsTypes()); } @Test public void expandedPip_enableExpansion_enable() { - assumeTelevision(); // PiP has expanded PiP disabled. - mActionsProvider.updateExpansionEnabled(false); - mActionsProvider.addListener(mMockListener); mActionsProvider.updateExpansionEnabled(true); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE), + getActionsTypes()); verify(mMockListener).onActionsChanged(/* added= */ 1, /* updated= */ 0, /* index= */ 3); } @Test public void expandedPip_enableExpansion_disable() { - assumeTelevision(); mActionsProvider.updateExpansionEnabled(true); mActionsProvider.addListener(mMockListener); mActionsProvider.updateExpansionEnabled(false); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE), + getActionsTypes()); verify(mMockListener).onActionsChanged(/* added= */ -1, /* updated= */ 0, /* index= */ 3); } @Test public void expandedPip_enableExpansion_AlreadyEnabled() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(true); - mActionsProvider.addListener(mMockListener); mActionsProvider.updateExpansionEnabled(true); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE), + getActionsTypes()); } private void check_expandedPip_updateExpansionState( @@ -167,8 +167,9 @@ public class TvPipActionProviderTest extends ShellTestCase { mActionsProvider.addListener(mMockListener); mActionsProvider.updatePipExpansionState(endExpansion); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE, ACTION_EXPAND_COLLAPSE), + getActionsTypes()); if (updateExpected) { verify(mMockListener).onActionsChanged(0, 1, 3); @@ -180,7 +181,6 @@ public class TvPipActionProviderTest extends ShellTestCase { @Test public void expandedPip_toggleExpansion_collapse() { - assumeTelevision(); check_expandedPip_updateExpansionState( /* startExpansion= */ true, /* endExpansion= */ false, @@ -189,7 +189,6 @@ public class TvPipActionProviderTest extends ShellTestCase { @Test public void expandedPip_toggleExpansion_expand() { - assumeTelevision(); check_expandedPip_updateExpansionState( /* startExpansion= */ false, /* endExpansion= */ true, @@ -198,7 +197,6 @@ public class TvPipActionProviderTest extends ShellTestCase { @Test public void expandedPiP_updateExpansionState_alreadyExpanded() { - assumeTelevision(); check_expandedPip_updateExpansionState( /* startExpansion= */ true, /* endExpansion= */ true, @@ -207,7 +205,6 @@ public class TvPipActionProviderTest extends ShellTestCase { @Test public void expandedPiP_updateExpansionState_alreadyCollapsed() { - assumeTelevision(); check_expandedPip_updateExpansionState( /* startExpansion= */ false, /* endExpansion= */ false, @@ -216,8 +213,6 @@ public class TvPipActionProviderTest extends ShellTestCase { @Test public void regularPiP_updateExpansionState_setCollapsed() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); mActionsProvider.updatePipExpansionState(/* expanded= */ false); mActionsProvider.addListener(mMockListener); @@ -229,153 +224,207 @@ public class TvPipActionProviderTest extends ShellTestCase { @Test public void customActions_added() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); mActionsProvider.addListener(mMockListener); mActionsProvider.setAppActions(createRemoteActions(2), null); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, - ACTION_MOVE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, + ACTION_MOVE), + getActionsTypes()); verify(mMockListener).onActionsChanged(/* added= */ 2, /* updated= */ 0, /* index= */ 2); } @Test public void customActions_replacedMore() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); mActionsProvider.setAppActions(createRemoteActions(2), null); mActionsProvider.addListener(mMockListener); mActionsProvider.setAppActions(createRemoteActions(3), null); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, - ACTION_CUSTOM, ACTION_MOVE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, + ACTION_CUSTOM, ACTION_MOVE), + getActionsTypes()); verify(mMockListener).onActionsChanged(/* added= */ 1, /* updated= */ 2, /* index= */ 2); } @Test public void customActions_replacedLess() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); mActionsProvider.setAppActions(createRemoteActions(2), null); mActionsProvider.addListener(mMockListener); - mActionsProvider.setAppActions(createRemoteActions(0), null); + mActionsProvider.setAppActions(EMPTY_LIST, null); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE), + getActionsTypes()); verify(mMockListener).onActionsChanged(/* added= */ -2, /* updated= */ 0, /* index= */ 2); } @Test public void customCloseAdded() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); - List<RemoteAction> customActions = new ArrayList<>(); mActionsProvider.setAppActions(customActions, null); mActionsProvider.addListener(mMockListener); - mActionsProvider.setAppActions(customActions, createRemoteAction(0)); + mActionsProvider.setAppActions(customActions, createRemoteAction()); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_MOVE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_MOVE), + getActionsTypes()); verify(mMockListener).onActionsChanged(/* added= */ 0, /* updated= */ 1, /* index= */ 1); } @Test public void customClose_matchesOtherCustomAction() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); - List<RemoteAction> customActions = createRemoteActions(2); - RemoteAction customClose = createRemoteAction(/* id= */ 10); + RemoteAction customClose = createRemoteAction(); customActions.add(customClose); mActionsProvider.addListener(mMockListener); mActionsProvider.setAppActions(customActions, customClose); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, - ACTION_MOVE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, + ACTION_MOVE), + getActionsTypes()); verify(mMockListener).onActionsChanged(/* added= */ 0, /* updated= */ 1, /* index= */ 1); verify(mMockListener).onActionsChanged(/* added= */ 2, /* updated= */ 0, /* index= */ 2); } @Test public void mediaActions_added_whileCustomActionsExist() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); mActionsProvider.setAppActions(createRemoteActions(2), null); mActionsProvider.addListener(mMockListener); mActionsProvider.onMediaActionsChanged(createRemoteActions(3)); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, - ACTION_MOVE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, + ACTION_MOVE), + getActionsTypes()); verify(mMockListener, times(0)).onActionsChanged(anyInt(), anyInt(), anyInt()); } @Test public void customActions_removed_whileMediaActionsExist() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); mActionsProvider.onMediaActionsChanged(createRemoteActions(2)); mActionsProvider.setAppActions(createRemoteActions(3), null); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, + ACTION_CUSTOM, ACTION_MOVE), + getActionsTypes()); + mActionsProvider.addListener(mMockListener); - mActionsProvider.setAppActions(createRemoteActions(0), null); + mActionsProvider.setAppActions(EMPTY_LIST, null); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, - ACTION_MOVE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, + ACTION_MOVE), + getActionsTypes()); verify(mMockListener).onActionsChanged(/* added= */ -1, /* updated= */ 2, /* index= */ 2); } @Test public void customCloseOnly_mediaActionsShowing() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); mActionsProvider.onMediaActionsChanged(createRemoteActions(2)); mActionsProvider.addListener(mMockListener); - mActionsProvider.setAppActions(createRemoteActions(0), createRemoteAction(5)); + mActionsProvider.setAppActions(EMPTY_LIST, createRemoteAction()); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, - ACTION_MOVE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, + ACTION_MOVE), + getActionsTypes()); verify(mMockListener).onActionsChanged(/* added= */ 0, /* updated= */ 1, /* index= */ 1); } @Test public void customActions_showDisabledActions() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); - List<RemoteAction> customActions = createRemoteActions(2); customActions.get(0).setEnabled(false); mActionsProvider.setAppActions(customActions, null); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, - ACTION_MOVE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, + ACTION_MOVE), + getActionsTypes()); } @Test public void mediaActions_hideDisabledActions() { - assumeTelevision(); - mActionsProvider.updateExpansionEnabled(false); + List<RemoteAction> customActions = createRemoteActions(2); + customActions.get(0).setEnabled(false); + mActionsProvider.onMediaActionsChanged(customActions); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_MOVE), + getActionsTypes()); + } + + @Test + public void reset_mediaActions() { List<RemoteAction> customActions = createRemoteActions(2); customActions.get(0).setEnabled(false); mActionsProvider.onMediaActionsChanged(customActions); - assertTrue(checkActionsMatch(mActionsProvider.getActionsList(), - new int[]{ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_MOVE})); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_MOVE), + getActionsTypes()); + + mActionsProvider.reset(); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE), + getActionsTypes()); + } + + @Test + public void reset_customActions() { + List<RemoteAction> customActions = createRemoteActions(2); + customActions.get(0).setEnabled(false); + mActionsProvider.setAppActions(customActions, null); + + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, + ACTION_MOVE), + getActionsTypes()); + + mActionsProvider.reset(); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE), + getActionsTypes()); + } + + @Test + public void reset_customClose() { + mActionsProvider.setAppActions(EMPTY_LIST, createRemoteAction()); + + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_MOVE), + getActionsTypes()); + + mActionsProvider.reset(); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE), + getActionsTypes()); + } + + @Test + public void reset_All() { + mActionsProvider.setAppActions(createRemoteActions(2), createRemoteAction()); + mActionsProvider.onMediaActionsChanged(createRemoteActions(3)); + + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CUSTOM_CLOSE, ACTION_CUSTOM, ACTION_CUSTOM, + ACTION_MOVE), + getActionsTypes()); + + mActionsProvider.reset(); + assertActionTypes( + Arrays.asList(ACTION_FULLSCREEN, ACTION_CLOSE, ACTION_MOVE), + getActionsTypes()); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java index 82fe5f2f6afc..974539f23b80 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java @@ -53,9 +53,8 @@ public class TvPipGravityTest extends ShellTestCase { @Before public void setUp() { - if (!isTelevision()) { - return; - } + assumeTelevision(); + MockitoAnnotations.initMocks(this); mPipDisplayLayoutState = new PipDisplayLayoutState(mContext); mSizeSpecSource = new LegacySizeSpecSource(mContext, mPipDisplayLayoutState); @@ -101,20 +100,22 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void regularPip_defaultGravity() { - assumeTelevision(); checkGravity(mTvPipBoundsState.getDefaultGravity(), Gravity.RIGHT | Gravity.BOTTOM); } @Test + public void regularPip_defaultTvPipGravity() { + checkGravity(mTvPipBoundsState.getTvPipGravity(), Gravity.RIGHT | Gravity.BOTTOM); + } + + @Test public void regularPip_defaultGravity_RTL() { - assumeTelevision(); setRTL(true); checkGravity(mTvPipBoundsState.getDefaultGravity(), Gravity.LEFT | Gravity.BOTTOM); } @Test public void updateGravity_expand_vertical() { - assumeTelevision(); // Vertical expanded PiP. mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true); @@ -130,7 +131,6 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void updateGravity_expand_horizontal() { - assumeTelevision(); // Horizontal expanded PiP. mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true); @@ -146,7 +146,6 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void updateGravity_collapse() { - assumeTelevision(); // Vertical expansion mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true); assertGravityAfterCollapse(Gravity.CENTER_VERTICAL | Gravity.RIGHT, @@ -164,7 +163,6 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void updateGravity_collapse_RTL() { - assumeTelevision(); setRTL(true); // Horizontal expansion @@ -177,7 +175,6 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void updateGravity_expand_collapse() { - assumeTelevision(); // Vertical expanded PiP. mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true); @@ -197,7 +194,6 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void updateGravity_expand_move_collapse() { - assumeTelevision(); // Vertical expanded PiP. mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true); expandMoveCollapseCheck(Gravity.TOP | Gravity.RIGHT, KEYCODE_DPAD_LEFT, @@ -230,7 +226,6 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void updateGravity_move_regular_valid() { - assumeTelevision(); mTvPipBoundsState.setTvPipGravity(Gravity.BOTTOM | Gravity.RIGHT); // clockwise moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.BOTTOM | Gravity.LEFT, true); @@ -246,7 +241,6 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void updateGravity_move_expanded_valid() { - assumeTelevision(); mTvPipBoundsState.setTvPipExpanded(true); // Vertical expanded PiP. @@ -264,7 +258,6 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void updateGravity_move_regular_invalid() { - assumeTelevision(); int gravity = Gravity.BOTTOM | Gravity.RIGHT; mTvPipBoundsState.setTvPipGravity(gravity); moveAndCheckGravity(KEYCODE_DPAD_DOWN, gravity, false); @@ -288,7 +281,6 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void updateGravity_move_expanded_invalid() { - assumeTelevision(); mTvPipBoundsState.setTvPipExpanded(true); // Vertical expanded PiP. @@ -318,7 +310,6 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void previousCollapsedGravity_defaultValue() { - assumeTelevision(); assertEquals(mTvPipBoundsState.getTvPipPreviousCollapsedGravity(), mTvPipBoundsState.getDefaultGravity()); setRTL(true); @@ -328,7 +319,6 @@ public class TvPipGravityTest extends ShellTestCase { @Test public void previousCollapsedGravity_changes_on_RTL() { - assumeTelevision(); mTvPipBoundsState.setTvPipPreviousCollapsedGravity(Gravity.TOP | Gravity.LEFT); setRTL(true); assertEquals(mTvPipBoundsState.getTvPipPreviousCollapsedGravity(), diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java index 3a08d32bc430..e26dc7c10989 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipMenuControllerTest.java @@ -25,18 +25,24 @@ import static com.android.wm.shell.pip.tv.TvPipMenuController.MODE_NO_MENU; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.os.Handler; +import android.os.Looper; import android.view.SurfaceControl; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnWindowFocusChangeListener; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.SystemWindows; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -50,28 +56,38 @@ public class TvPipMenuControllerTest extends ShellTestCase { @Mock private SystemWindows mMockSystemWindows; @Mock - private SurfaceControl mMockPipLeash; - @Mock - private Handler mMockHandler; - @Mock - private TvPipActionsProvider mMockActionsProvider; - @Mock private TvPipMenuView mMockTvPipMenuView; @Mock private TvPipBackgroundView mMockTvPipBackgroundView; + private Handler mMainHandler; private TvPipMenuController mTvPipMenuController; + private OnWindowFocusChangeListener mFocusChangeListener; @Before public void setUp() { assumeTrue(isTelevision()); MockitoAnnotations.initMocks(this); + mMainHandler = new Handler(Looper.getMainLooper()); + + final ViewTreeObserver mockMenuTreeObserver = mock(ViewTreeObserver.class); + doReturn(mockMenuTreeObserver).when(mMockTvPipMenuView).getViewTreeObserver(); mTvPipMenuController = new TestTvPipMenuController(); mTvPipMenuController.setDelegate(mMockDelegate); - mTvPipMenuController.setTvPipActionsProvider(mMockActionsProvider); - mTvPipMenuController.attach(mMockPipLeash); + mTvPipMenuController.setTvPipActionsProvider(mock(TvPipActionsProvider.class)); + mTvPipMenuController.attach(mock(SurfaceControl.class)); + mFocusChangeListener = captureFocusChangeListener(mockMenuTreeObserver); + } + + private OnWindowFocusChangeListener captureFocusChangeListener( + ViewTreeObserver mockTreeObserver) { + final ArgumentCaptor<OnWindowFocusChangeListener> focusChangeListenerCaptor = + ArgumentCaptor.forClass(OnWindowFocusChangeListener.class); + verify(mockTreeObserver).addOnWindowFocusChangeListener( + focusChangeListenerCaptor.capture()); + return focusChangeListenerCaptor.getValue(); } @Test @@ -81,24 +97,25 @@ public class TvPipMenuControllerTest extends ShellTestCase { @Test public void testSwitch_FromNoMenuMode_ToMoveMode() { - showAndAssertMoveMenu(); + showAndAssertMoveMenu(true); } @Test public void testSwitch_FromNoMenuMode_ToAllActionsMode() { - showAndAssertAllActionsMenu(); + showAndAssertAllActionsMenu(true); } @Test public void testSwitch_FromMoveMode_ToAllActionsMode() { - showAndAssertMoveMenu(); - showAndAssertAllActionsMenu(); + showAndAssertMoveMenu(true); + showAndAssertAllActionsMenu(false); + verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test public void testSwitch_FromAllActionsMode_ToMoveMode() { - showAndAssertAllActionsMenu(); - showAndAssertMoveMenu(); + showAndAssertAllActionsMenu(true); + showAndAssertMoveMenu(false); } @Test @@ -110,187 +127,282 @@ public class TvPipMenuControllerTest extends ShellTestCase { @Test public void testCloseMenu_MoveMode() { - showAndAssertMoveMenu(); + showAndAssertMoveMenu(true); - closeMenuAndAssertMenuClosed(); + closeMenuAndAssertMenuClosed(true); verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test public void testCloseMenu_AllActionsMode() { - showAndAssertAllActionsMenu(); + showAndAssertAllActionsMenu(true); - closeMenuAndAssertMenuClosed(); + closeMenuAndAssertMenuClosed(true); + } + + @Test + public void testCloseMenu_MoveModeFollowedByMoveMode() { + showAndAssertMoveMenu(true); + showAndAssertMoveMenu(false); + + closeMenuAndAssertMenuClosed(true); + verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test public void testCloseMenu_MoveModeFollowedByAllActionsMode() { - showAndAssertMoveMenu(); - showAndAssertAllActionsMenu(); + showAndAssertMoveMenu(true); + showAndAssertAllActionsMenu(false); verify(mMockDelegate, times(2)).onInMoveModeChanged(); - closeMenuAndAssertMenuClosed(); + closeMenuAndAssertMenuClosed(true); } @Test public void testCloseMenu_AllActionsModeFollowedByMoveMode() { - showAndAssertAllActionsMenu(); - showAndAssertMoveMenu(); + showAndAssertAllActionsMenu(true); + showAndAssertMoveMenu(false); - closeMenuAndAssertMenuClosed(); + closeMenuAndAssertMenuClosed(true); verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test - public void testExitMoveMode_NoMenuMode() { - mTvPipMenuController.onExitMoveMode(); + public void testCloseMenu_AllActionsModeFollowedByAllActionsMode() { + showAndAssertAllActionsMenu(true); + showAndAssertAllActionsMenu(false); + + closeMenuAndAssertMenuClosed(true); + verify(mMockDelegate, never()).onInMoveModeChanged(); + } + + @Test + public void testExitMenuMode_NoMenuMode() { + mTvPipMenuController.onExitCurrentMenuMode(); assertMenuIsOpen(false); verify(mMockDelegate, never()).onMenuClosed(); + verify(mMockDelegate, never()).onInMoveModeChanged(); } @Test - public void testExitMoveMode_MoveMode() { - showAndAssertMoveMenu(); + public void testExitMenuMode_MoveMode() { + showAndAssertMoveMenu(true); - mTvPipMenuController.onExitMoveMode(); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); assertMenuClosed(); verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test - public void testExitMoveMode_AllActionsMode() { - showAndAssertAllActionsMenu(); - - mTvPipMenuController.onExitMoveMode(); - assertMenuIsInAllActionsMode(); + public void testExitMenuMode_AllActionsMode() { + showAndAssertAllActionsMenu(true); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); } @Test - public void testExitMoveMode_AllActionsModeFollowedByMoveMode() { - showAndAssertAllActionsMenu(); - showAndAssertMoveMenu(); + public void testExitMenuMode_AllActionsModeFollowedByMoveMode() { + showAndAssertAllActionsMenu(true); + showAndAssertMoveMenu(false); - mTvPipMenuController.onExitMoveMode(); - assertMenuIsInAllActionsMode(); + mTvPipMenuController.onExitCurrentMenuMode(); + assertSwitchedToAllActionsMode(2); verify(mMockDelegate, times(2)).onInMoveModeChanged(); - verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(false)); - verify(mMockTvPipBackgroundView, times(2)).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU)); - } - @Test - public void testOnBackPress_NoMenuMode() { - mTvPipMenuController.onBackPress(); - assertMenuIsOpen(false); - verify(mMockDelegate, never()).onMenuClosed(); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); } @Test - public void testOnBackPress_MoveMode() { - showAndAssertMoveMenu(); + public void testExitMenuMode_AllActionsModeFollowedByAllActionsMode() { + showAndAssertAllActionsMenu(true); + showAndAssertAllActionsMenu(false); - pressBackAndAssertMenuClosed(); - verify(mMockDelegate, times(2)).onInMoveModeChanged(); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); + verify(mMockDelegate, never()).onInMoveModeChanged(); } @Test - public void testOnBackPress_AllActionsMode() { - showAndAssertAllActionsMenu(); - - pressBackAndAssertMenuClosed(); - } + public void testExitMenuMode_MoveModeFollowedByAllActionsMode() { + showAndAssertMoveMenu(true); - @Test - public void testOnBackPress_MoveModeFollowedByAllActionsMode() { - showAndAssertMoveMenu(); - showAndAssertAllActionsMenu(); + showAndAssertAllActionsMenu(false); verify(mMockDelegate, times(2)).onInMoveModeChanged(); - pressBackAndAssertMenuClosed(); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); } @Test - public void testOnBackPress_AllActionsModeFollowedByMoveMode() { - showAndAssertAllActionsMenu(); - showAndAssertMoveMenu(); + public void testExitMenuMode_MoveModeFollowedByMoveMode() { + showAndAssertMoveMenu(true); + showAndAssertMoveMenu(false); - mTvPipMenuController.onBackPress(); - assertMenuIsInAllActionsMode(); + mTvPipMenuController.onExitCurrentMenuMode(); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); verify(mMockDelegate, times(2)).onInMoveModeChanged(); - verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(false)); - verify(mMockTvPipBackgroundView, times(2)).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU)); - - pressBackAndAssertMenuClosed(); } @Test public void testOnPipMovement_NoMenuMode() { - assertPipMoveSuccessful(false, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE)); + moveAndAssertMoveSuccessful(false); } @Test public void testOnPipMovement_MoveMode() { - showAndAssertMoveMenu(); - assertPipMoveSuccessful(true, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE)); - verify(mMockDelegate).movePip(eq(TEST_MOVE_KEYCODE)); + showAndAssertMoveMenu(true); + moveAndAssertMoveSuccessful(true); } @Test public void testOnPipMovement_AllActionsMode() { - showAndAssertAllActionsMenu(); - assertPipMoveSuccessful(false, mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE)); + showAndAssertAllActionsMenu(true); + moveAndAssertMoveSuccessful(false); } @Test - public void testOnPipWindowFocusChanged_NoMenuMode() { - mTvPipMenuController.onPipWindowFocusChanged(false); - assertMenuIsOpen(false); + public void testUnexpectedFocusChanges() { + mFocusChangeListener.onWindowFocusChanged(true); + assertSwitchedToAllActionsMode(1); + + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(); + + showAndAssertMoveMenu(true); + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(2); + verify(mMockDelegate, times(2)).onInMoveModeChanged(); } @Test - public void testOnPipWindowFocusChanged_MoveMode() { - showAndAssertMoveMenu(); - mTvPipMenuController.onPipWindowFocusChanged(false); - assertMenuClosed(); + public void testAsyncScenario_AllActionsModeRequestFollowedByAsyncMoveModeRequest() { + mTvPipMenuController.showMenu(); + // Artificially delaying the focus change update and adding a move request to simulate an + // async problematic situation. + mTvPipMenuController.showMovementMenu(); + // The first focus change update arrives + mFocusChangeListener.onWindowFocusChanged(true); + + // We expect that the TvPipMenuController will directly switch to the "pending" menu mode + // - MODE_MOVE_MENU, because no change of focus is needed. + assertSwitchedToMoveMode(); + } + + @Test + public void testAsyncScenario_MoveModeRequestFollowedByAsyncAllActionsModeRequest() { + mTvPipMenuController.showMovementMenu(); + mTvPipMenuController.showMenu(); + + mFocusChangeListener.onWindowFocusChanged(true); + assertSwitchedToAllActionsMode(1); + verify(mMockDelegate, never()).onInMoveModeChanged(); } @Test - public void testOnPipWindowFocusChanged_AllActionsMode() { - showAndAssertAllActionsMenu(); - mTvPipMenuController.onPipWindowFocusChanged(false); + public void testAsyncScenario_DropObsoleteIntermediateModeSwitchRequests() { + mTvPipMenuController.showMovementMenu(); + mTvPipMenuController.closeMenu(); + + // Focus change from showMovementMenu() call. + mFocusChangeListener.onWindowFocusChanged(true); + assertSwitchedToMoveMode(); + verify(mMockDelegate).onInMoveModeChanged(); + + // Focus change from closeMenu() call. + mFocusChangeListener.onWindowFocusChanged(false); assertMenuClosed(); + verify(mMockDelegate, times(2)).onInMoveModeChanged(); + + // Unexpected focus gain should open MODE_ALL_ACTIONS_MENU. + mFocusChangeListener.onWindowFocusChanged(true); + assertSwitchedToAllActionsMode(1); + + mTvPipMenuController.closeMenu(); + mTvPipMenuController.showMovementMenu(); + + assertSwitchedToMoveMode(2); + + mFocusChangeListener.onWindowFocusChanged(false); + assertMenuClosed(2); + + // Closing the menu resets the default menu mode, so the next focus gain opens the menu in + // the default mode - MODE_ALL_ACTIONS_MENU. + mFocusChangeListener.onWindowFocusChanged(true); + assertSwitchedToAllActionsMode(2); + verify(mMockDelegate, times(4)).onInMoveModeChanged(); + } - private void showAndAssertMoveMenu() { + private void showAndAssertMoveMenu(boolean focusChange) { mTvPipMenuController.showMovementMenu(); + if (focusChange) { + mFocusChangeListener.onWindowFocusChanged(true); + } + assertSwitchedToMoveMode(); + } + + private void assertSwitchedToMoveMode() { + assertSwitchedToMoveMode(1); + } + + private void assertSwitchedToMoveMode(int times) { assertMenuIsInMoveMode(); - verify(mMockDelegate).onInMoveModeChanged(); - verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_MOVE_MENU), eq(false)); - verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_MOVE_MENU)); + verify(mMockDelegate, times(2 * times - 1)).onInMoveModeChanged(); + verify(mMockTvPipMenuView, times(times)).transitionToMenuMode(eq(MODE_MOVE_MENU)); + verify(mMockTvPipBackgroundView, times(times)).transitionToMenuMode(eq(MODE_MOVE_MENU)); + } + + private void showAndAssertAllActionsMenu(boolean focusChange) { + showAndAssertAllActionsMenu(focusChange, 1); } - private void showAndAssertAllActionsMenu() { + private void showAndAssertAllActionsMenu(boolean focusChange, int times) { mTvPipMenuController.showMenu(); + if (focusChange) { + mFocusChangeListener.onWindowFocusChanged(true); + } + + assertSwitchedToAllActionsMode(times); + } + + private void assertSwitchedToAllActionsMode(int times) { assertMenuIsInAllActionsMode(); - verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU), eq(true)); - verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU)); + verify(mMockTvPipMenuView, times(times)) + .transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU)); + verify(mMockTvPipBackgroundView, times(times)) + .transitionToMenuMode(eq(MODE_ALL_ACTIONS_MENU)); } - private void closeMenuAndAssertMenuClosed() { + private void closeMenuAndAssertMenuClosed(boolean focusChange) { mTvPipMenuController.closeMenu(); + if (focusChange) { + mFocusChangeListener.onWindowFocusChanged(false); + } assertMenuClosed(); } - private void pressBackAndAssertMenuClosed() { - mTvPipMenuController.onBackPress(); - assertMenuClosed(); + private void moveAndAssertMoveSuccessful(boolean expectedSuccess) { + mTvPipMenuController.onPipMovement(TEST_MOVE_KEYCODE); + verify(mMockDelegate, times(expectedSuccess ? 1 : 0)).movePip(eq(TEST_MOVE_KEYCODE)); } private void assertMenuClosed() { + assertMenuClosed(1); + } + + private void assertMenuClosed(int times) { assertMenuIsOpen(false); - verify(mMockDelegate).onMenuClosed(); - verify(mMockTvPipMenuView).transitionToMenuMode(eq(MODE_NO_MENU), eq(false)); - verify(mMockTvPipBackgroundView).transitionToMenuMode(eq(MODE_NO_MENU)); + verify(mMockDelegate, times(times)).onMenuClosed(); + verify(mMockTvPipMenuView, times(times)).transitionToMenuMode(eq(MODE_NO_MENU)); + verify(mMockTvPipBackgroundView, times(times)).transitionToMenuMode(eq(MODE_NO_MENU)); } private void assertMenuIsOpen(boolean open) { @@ -312,15 +424,10 @@ public class TvPipMenuControllerTest extends ShellTestCase { assertMenuIsOpen(true); } - private void assertPipMoveSuccessful(boolean expected, boolean actual) { - assertTrue("Should " + (expected ? "" : "not ") + "move PiP when the menu is in mode " - + mTvPipMenuController.getMenuModeString(), expected == actual); - } - private class TestTvPipMenuController extends TvPipMenuController { TestTvPipMenuController() { - super(mContext, mMockTvPipBoundsState, mMockSystemWindows, mMockHandler); + super(mContext, mMockTvPipBoundsState, mMockSystemWindows, mMainHandler); } @Override diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt index baa06f2f0c45..bbd65be9abda 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt @@ -24,6 +24,7 @@ import android.window.IWindowContainerToken import android.window.WindowContainerToken import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50 import com.android.wm.shell.util.GroupedRecentTaskInfo import com.android.wm.shell.util.GroupedRecentTaskInfo.CREATOR import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM @@ -123,6 +124,7 @@ class GroupedRecentTaskInfoTest : ShellTestCase() { assertThat(recentTaskInfoParcel.taskInfo2).isNotNull() assertThat(recentTaskInfoParcel.taskInfo2!!.taskId).isEqualTo(2) assertThat(recentTaskInfoParcel.splitBounds).isNotNull() + assertThat(recentTaskInfoParcel.splitBounds!!.snapPosition).isEqualTo(SNAP_TO_50_50) } @Test @@ -156,7 +158,7 @@ class GroupedRecentTaskInfoTest : ShellTestCase() { private fun splitTasksGroupInfo(): GroupedRecentTaskInfo { val task1 = createTaskInfo(id = 1) val task2 = createTaskInfo(id = 2) - val splitBounds = SplitBounds(Rect(), Rect(), 1, 2) + val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_50_50) return GroupedRecentTaskInfo.forSplitTasks(task1, task2, splitBounds) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index 2c69522413d5..10e9e11e9004 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -17,10 +17,12 @@ package com.android.wm.shell.recents; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -172,10 +174,10 @@ public class RecentTasksControllerTest extends ShellTestCase { // Verify only one update if the split info is the same SplitBounds bounds1 = new SplitBounds(new Rect(0, 0, 50, 50), - new Rect(50, 50, 100, 100), t1.taskId, t2.taskId); + new Rect(50, 50, 100, 100), t1.taskId, t2.taskId, SNAP_TO_50_50); mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds1); SplitBounds bounds2 = new SplitBounds(new Rect(0, 0, 50, 50), - new Rect(50, 50, 100, 100), t1.taskId, t2.taskId); + new Rect(50, 50, 100, 100), t1.taskId, t2.taskId, SNAP_TO_50_50); mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds2); verify(mRecentTasksController, times(1)).notifyRecentTasksChanged(); } @@ -206,8 +208,10 @@ public class RecentTasksControllerTest extends ShellTestCase { setRawList(t1, t2, t3, t4, t5, t6); // Mark a couple pairs [t2, t4], [t3, t5] - SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4); - SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5); + SplitBounds pair1Bounds = + new SplitBounds(new Rect(), new Rect(), 2, 4, SNAP_TO_50_50); + SplitBounds pair2Bounds = + new SplitBounds(new Rect(), new Rect(), 3, 5, SNAP_TO_50_50); mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds); mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds); @@ -235,8 +239,10 @@ public class RecentTasksControllerTest extends ShellTestCase { setRawList(t1, t2, t3, t4, t5, t6); // Mark a couple pairs [t2, t4], [t3, t5] - SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4); - SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5); + SplitBounds pair1Bounds = + new SplitBounds(new Rect(), new Rect(), 2, 4, SNAP_TO_50_50); + SplitBounds pair2Bounds = + new SplitBounds(new Rect(), new Rect(), 3, 5, SNAP_TO_50_50); mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds); mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds); @@ -256,7 +262,7 @@ public class RecentTasksControllerTest extends ShellTestCase { public void testGetRecentTasks_hasActiveDesktopTasks_proto2Enabled_groupFreeformTasks() { StaticMockitoSession mockitoSession = mockitoSession().mockStatic( DesktopModeStatus.class).startMocking(); - when(DesktopModeStatus.isProto2Enabled()).thenReturn(true); + when(DesktopModeStatus.isEnabled()).thenReturn(true); ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1); ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2); @@ -296,7 +302,7 @@ public class RecentTasksControllerTest extends ShellTestCase { public void testGetRecentTasks_hasActiveDesktopTasks_proto2Disabled_doNotGroupFreeformTasks() { StaticMockitoSession mockitoSession = mockitoSession().mockStatic( DesktopModeStatus.class).startMocking(); - when(DesktopModeStatus.isProto2Enabled()).thenReturn(false); + when(DesktopModeStatus.isEnabled()).thenReturn(false); ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1); ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2); @@ -333,7 +339,8 @@ public class RecentTasksControllerTest extends ShellTestCase { setRawList(t1, t2, t3); // Add a pair - SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 3); + SplitBounds pair1Bounds = + new SplitBounds(new Rect(), new Rect(), 2, 3, SNAP_TO_50_50); mRecentTasksController.addSplitPair(t2.taskId, t3.taskId, pair1Bounds); reset(mRecentTasksController); @@ -367,6 +374,37 @@ public class RecentTasksControllerTest extends ShellTestCase { verify(mRecentTasksController).notifyRecentTasksChanged(); } + @Test + public void getNullSplitBoundsNonSplitTask() { + SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(3); + assertNull("splitBounds should be null for non-split task", sb); + } + + @Test + public void getNullSplitBoundsInvalidTask() { + SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(INVALID_TASK_ID); + assertNull("splitBounds should be null for invalid taskID", sb); + } + + @Test + public void getSplitBoundsForSplitTask() { + SplitBounds pair1Bounds = mock(SplitBounds.class); + SplitBounds pair2Bounds = mock(SplitBounds.class); + + mRecentTasksController.addSplitPair(1, 2, pair1Bounds); + mRecentTasksController.addSplitPair(4, 3, pair2Bounds); + + SplitBounds splitBounds2 = mRecentTasksController.getSplitBoundsForTaskId(2); + SplitBounds splitBounds1 = mRecentTasksController.getSplitBoundsForTaskId(1); + assertEquals("Different splitBounds for same pair", splitBounds1, splitBounds2); + assertEquals(splitBounds1, pair1Bounds); + + SplitBounds splitBounds3 = mRecentTasksController.getSplitBoundsForTaskId(3); + SplitBounds splitBounds4 = mRecentTasksController.getSplitBoundsForTaskId(4); + assertEquals("Different splitBounds for same pair", splitBounds3, splitBounds4); + assertEquals(splitBounds4, pair2Bounds); + } + /** * Helper to create a task with a given task id. */ diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java index 50d02ae0dccd..b790aee6fb0e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java @@ -1,5 +1,7 @@ package com.android.wm.shell.recents; +import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -44,21 +46,21 @@ public class SplitBoundsTest extends ShellTestCase { @Test public void testVerticalStacked() { SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect, - TASK_ID_1, TASK_ID_2); + TASK_ID_1, TASK_ID_2, SNAP_TO_50_50); assertTrue(ssb.appsStackedVertically); } @Test public void testHorizontalStacked() { SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect, - TASK_ID_1, TASK_ID_2); + TASK_ID_1, TASK_ID_2, SNAP_TO_50_50); assertFalse(ssb.appsStackedVertically); } @Test public void testHorizontalDividerBounds() { SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect, - TASK_ID_1, TASK_ID_2); + TASK_ID_1, TASK_ID_2, SNAP_TO_50_50); Rect dividerBounds = ssb.visualDividerBounds; assertEquals(0, dividerBounds.left); assertEquals(DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2, dividerBounds.top); @@ -69,7 +71,7 @@ public class SplitBoundsTest extends ShellTestCase { @Test public void testVerticalDividerBounds() { SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect, - TASK_ID_1, TASK_ID_2); + TASK_ID_1, TASK_ID_2, SNAP_TO_50_50); Rect dividerBounds = ssb.visualDividerBounds; assertEquals(DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2, dividerBounds.left); assertEquals(0, dividerBounds.top); @@ -80,7 +82,7 @@ public class SplitBoundsTest extends ShellTestCase { @Test public void testEqualVerticalTaskPercent() { SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect, - TASK_ID_1, TASK_ID_2); + TASK_ID_1, TASK_ID_2, SNAP_TO_50_50); float topPercentSpaceTaken = (float) (DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2) / DEVICE_LENGTH; assertEquals(topPercentSpaceTaken, ssb.topTaskPercent, 0.01); } @@ -88,7 +90,7 @@ public class SplitBoundsTest extends ShellTestCase { @Test public void testEqualHorizontalTaskPercent() { SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect, - TASK_ID_1, TASK_ID_2); + TASK_ID_1, TASK_ID_2, SNAP_TO_50_50); float leftPercentSpaceTaken = (float) (DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2) / DEVICE_WIDTH; assertEquals(leftPercentSpaceTaken, ssb.leftTaskPercent, 0.01); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java index 568db919818c..99cd4f391153 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java @@ -36,6 +36,7 @@ import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -218,8 +219,7 @@ public class SplitScreenControllerTests extends ShellTestCase { } @Test - public void startIntent_multiInstancesSupported_startTaskInBackgroundBeforeSplitActivated() { - doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any()); + public void startIntent_multiInstancesNotSupported_startTaskInBackgroundBeforeSplitActivated() { doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any()); Intent startIntent = createStartIntent("startActivity"); PendingIntent pendingIntent = @@ -237,6 +237,8 @@ public class SplitScreenControllerTests extends ShellTestCase { verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT), isNull()); + verify(mSplitScreenController, never()).supportMultiInstancesSplit(any()); + verify(mStageCoordinator, never()).switchSplitPosition(any()); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index 5efd9ad97a3e..befc702b01aa 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -47,11 +47,8 @@ import static org.mockito.Mockito.spy; import android.annotation.NonNull; import android.app.ActivityManager; import android.os.IBinder; -import android.os.RemoteException; import android.view.SurfaceControl; import android.view.SurfaceSession; -import android.window.IRemoteTransition; -import android.window.IRemoteTransitionFinishedCallback; import android.window.RemoteTransition; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; @@ -66,7 +63,6 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; -import com.android.wm.shell.TransitionInfoBuilder; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; @@ -77,6 +73,8 @@ import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitDecorManager; import com.android.wm.shell.common.split.SplitLayout; import com.android.wm.shell.transition.DefaultMixedHandler; +import com.android.wm.shell.transition.TestRemoteTransition; +import com.android.wm.shell.transition.TransitionInfoBuilder; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.WindowDecorViewModel; @@ -205,7 +203,31 @@ public class SplitTransitionTests extends ShellTestCase { // Make sure split-screen is now visible assertTrue(mStageCoordinator.isSplitScreenVisible()); - assertTrue(testRemote.mCalled); + assertTrue(testRemote.isCalled()); + } + + @Test + @UiThreadTest + public void testRemoteTransitionConsumed() { + // Omit side child change + TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0) + .addChange(TRANSIT_OPEN, mMainChild) + .build(); + TestRemoteTransition testRemote = new TestRemoteTransition(); + + IBinder transition = mSplitScreenTransitions.startEnterTransition( + TRANSIT_OPEN, new WindowContainerTransaction(), + new RemoteTransition(testRemote, "Test"), mStageCoordinator, + TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false); + mMainStage.onTaskAppeared(mMainChild, createMockSurface()); + boolean accepted = mStageCoordinator.startAnimation(transition, info, + mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), + mock(Transitions.TransitionFinishCallback.class)); + assertTrue(accepted); + + assertTrue(testRemote.isConsumed()); + } @Test @@ -468,24 +490,4 @@ public class SplitTransitionTests extends ShellTestCase { return out; } - class TestRemoteTransition extends IRemoteTransition.Stub { - boolean mCalled = false; - final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction(); - - @Override - public void startAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction startTransaction, - IRemoteTransitionFinishedCallback finishCallback) - throws RemoteException { - mCalled = true; - finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */); - } - - @Override - public void mergeAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, IBinder mergeTarget, - IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { - } - } - } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java index 0088051928fb..4afb29ecd98c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java @@ -43,6 +43,8 @@ import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; +import android.os.Handler; +import android.os.Looper; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.SurfaceControl; @@ -88,6 +90,10 @@ public class TaskViewTest extends ShellTestCase { SyncTransactionQueue mSyncQueue; @Mock Transitions mTransitions; + @Mock + Looper mViewLooper; + @Mock + Handler mViewHandler; SurfaceSession mSession; SurfaceControl mLeash; @@ -105,6 +111,8 @@ public class TaskViewTest extends ShellTestCase { .build(); mContext = getContext(); + doReturn(true).when(mViewLooper).isCurrentThread(); + doReturn(mViewLooper).when(mViewHandler).getLooper(); mTaskInfo = new ActivityManager.RunningTaskInfo(); mTaskInfo.token = mToken; @@ -132,6 +140,7 @@ public class TaskViewTest extends ShellTestCase { mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue)); mTaskView = new TaskView(mContext, mTaskViewTaskController); + mTaskView.setHandler(mViewHandler); mTaskView.setListener(mExecutor, mViewListener); } @@ -646,4 +655,17 @@ public class TaskViewTest extends ShellTestCase { assertThat(mTaskViewTaskController.getTaskInfo()).isNull(); } + + @Test + public void testOnTaskInfoChangedOnSameUiThread() { + mTaskViewTaskController.onTaskInfoChanged(mTaskInfo); + verify(mViewHandler, never()).post(any()); + } + + @Test + public void testOnTaskInfoChangedOnDifferentUiThread() { + doReturn(false).when(mViewLooper).isCurrentThread(); + mTaskViewTaskController.onTaskInfoChanged(mTaskInfo); + verify(mViewHandler).post(any()); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java index 03ed18c86608..050443914355 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java @@ -65,12 +65,6 @@ public class TaskViewTransitionsTest extends ShellTestCase { ActivityManager.RunningTaskInfo mTaskInfo; @Mock WindowContainerToken mToken; - @Mock - TaskViewTaskController mTaskViewTaskController2; - @Mock - ActivityManager.RunningTaskInfo mTaskInfo2; - @Mock - WindowContainerToken mToken2; TaskViewTransitions mTaskViewTransitions; @@ -86,16 +80,10 @@ public class TaskViewTransitionsTest extends ShellTestCase { mTaskInfo.token = mToken; mTaskInfo.taskId = 314; mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class); - when(mTaskViewTaskController.getTaskInfo()).thenReturn(mTaskInfo); - - mTaskInfo2 = new ActivityManager.RunningTaskInfo(); - mTaskInfo2.token = mToken2; - mTaskInfo2.taskId = 315; - mTaskInfo2.taskDescription = mock(ActivityManager.TaskDescription.class); - when(mTaskViewTaskController2.getTaskInfo()).thenReturn(mTaskInfo2); mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions)); mTaskViewTransitions.addTaskView(mTaskViewTaskController); + when(mTaskViewTaskController.getTaskInfo()).thenReturn(mTaskInfo); } @Test @@ -138,7 +126,7 @@ public class TaskViewTransitionsTest extends ShellTestCase { } @Test - public void testSetTaskBounds_taskVisibleWithPendingOpen_noTransaction() { + public void testSetTaskBounds_taskVisibleWithPending_noTransaction() { assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true); @@ -154,43 +142,6 @@ public class TaskViewTransitionsTest extends ShellTestCase { } @Test - public void testSetTaskBounds_taskVisibleWithPendingChange_transition() { - assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); - - mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true); - - // Consume the pending transition from visibility change - TaskViewTransitions.PendingTransition pending = - mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT); - assertThat(pending).isNotNull(); - mTaskViewTransitions.startAnimation(pending.mClaimed, - mock(TransitionInfo.class), - new SurfaceControl.Transaction(), - new SurfaceControl.Transaction(), - mock(Transitions.TransitionFinishCallback.class)); - // Verify it was consumed - TaskViewTransitions.PendingTransition checkPending = - mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT); - assertThat(checkPending).isNull(); - - // Test that set bounds creates a new transition - mTaskViewTransitions.setTaskBounds(mTaskViewTaskController, - new Rect(0, 0, 100, 100)); - assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE)) - .isNotNull(); - - // Test that set bounds again (with different bounds) creates another transition - mTaskViewTransitions.setTaskBounds(mTaskViewTaskController, - new Rect(0, 0, 300, 200)); - List<TaskViewTransitions.PendingTransition> pendingList = - mTaskViewTransitions.findAllPending(mTaskViewTaskController) - .stream() - .filter(pendingTransition -> pendingTransition.mType == TRANSIT_CHANGE) - .toList(); - assertThat(pendingList.size()).isEqualTo(2); - } - - @Test public void testSetTaskBounds_sameBounds_noTransaction() { assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); @@ -217,16 +168,6 @@ public class TaskViewTransitionsTest extends ShellTestCase { mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE); assertThat(pendingBounds).isNotNull(); - // Test that setting same bounds with in-flight transition doesn't cause another one - mTaskViewTransitions.setTaskBounds(mTaskViewTaskController, - new Rect(0, 0, 100, 100)); - List<TaskViewTransitions.PendingTransition> pendingList = - mTaskViewTransitions.findAllPending(mTaskViewTaskController) - .stream() - .filter(pendingTransition -> pendingTransition.mType == TRANSIT_CHANGE) - .toList(); - assertThat(pendingList.size()).isEqualTo(1); - // Consume the pending bounds transaction mTaskViewTransitions.startAnimation(pendingBounds.mClaimed, mock(TransitionInfo.class), @@ -246,42 +187,6 @@ public class TaskViewTransitionsTest extends ShellTestCase { assertThat(pendingBounds2).isNull(); } - - @Test - public void testSetTaskBounds_taskVisibleWithDifferentTaskViewPendingChange_transition() { - assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); - - mTaskViewTransitions.addTaskView(mTaskViewTaskController2); - - mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true); - - // Consume the pending transition from visibility change - TaskViewTransitions.PendingTransition pending = - mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT); - assertThat(pending).isNotNull(); - mTaskViewTransitions.startAnimation(pending.mClaimed, - mock(TransitionInfo.class), - new SurfaceControl.Transaction(), - new SurfaceControl.Transaction(), - mock(Transitions.TransitionFinishCallback.class)); - // Verify it was consumed - TaskViewTransitions.PendingTransition checkPending = - mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT); - assertThat(checkPending).isNull(); - - // Set the second taskview as visible & check that it has a pending transition - mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController2, true); - TaskViewTransitions.PendingTransition pending2 = - mTaskViewTransitions.findPending(mTaskViewTaskController2, TRANSIT_TO_FRONT); - assertThat(pending2).isNotNull(); - - // Test that set bounds on the first taskview will create a new transition - mTaskViewTransitions.setTaskBounds(mTaskViewTaskController, - new Rect(0, 0, 100, 100)); - assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE)) - .isNotNull(); - } - @Test public void testSetTaskVisibility_taskRemoved_noNPE() { mTaskViewTransitions.removeTaskView(mTaskViewTaskController); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index b32e0d6b4b39..da83d4c0a122 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -50,6 +50,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; @@ -92,13 +93,14 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; -import com.android.wm.shell.TransitionInfoBuilder; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; @@ -145,7 +147,9 @@ public class ShellTransitionTests extends ShellTestCase { final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class), mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor, mMainHandler, mAnimExecutor); - verify(shellInit, times(1)).addInitCallback(any(), eq(t)); + // One from Transitions, one from RootTaskDisplayAreaOrganizer + verify(shellInit).addInitCallback(any(), eq(t)); + verify(shellInit).addInitCallback(any(), isA(RootTaskDisplayAreaOrganizer.class)); } @Test @@ -285,6 +289,10 @@ public class ShellTransitionTests extends ShellTestCase { SurfaceControl.Transaction t, IBinder mergeTarget, IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { } + + @Override + public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException { + } }; IBinder transitToken = new Binder(); transitions.requestStartTransition(transitToken, @@ -427,6 +435,10 @@ public class ShellTransitionTests extends ShellTestCase { SurfaceControl.Transaction t, IBinder mergeTarget, IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { } + + @Override + public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException { + } }; TransitionFilter filter = new TransitionFilter(); @@ -473,6 +485,10 @@ public class ShellTransitionTests extends ShellTestCase { SurfaceControl.Transaction t, IBinder mergeTarget, IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { } + + @Override + public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException { + } }; final int transitType = TRANSIT_FIRST_CUSTOM + 1; @@ -1061,7 +1077,8 @@ public class ShellTransitionTests extends ShellTestCase { mTransactionPool, createTestDisplayController(), mMainExecutor, mMainHandler, mAnimExecutor); final RecentsTransitionHandler recentsHandler = - new RecentsTransitionHandler(shellInit, transitions, null); + new RecentsTransitionHandler(shellInit, transitions, + mock(RecentTasksController.class)); transitions.replaceDefaultHandlerForTest(mDefaultHandler); shellInit.init(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java new file mode 100644 index 000000000000..87330d2dc877 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.transition; + +import android.os.IBinder; +import android.os.RemoteException; +import android.view.SurfaceControl; +import android.window.IRemoteTransition; +import android.window.IRemoteTransitionFinishedCallback; +import android.window.TransitionInfo; +import android.window.WindowContainerTransaction; + +/** + * {@link IRemoteTransition} for testing purposes. + * Stores info about + * {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction, + * IRemoteTransitionFinishedCallback)} being called. + */ +public class TestRemoteTransition extends IRemoteTransition.Stub { + private boolean mCalled = false; + private boolean mConsumed = false; + final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction(); + + @Override + public void startAnimation(IBinder transition, TransitionInfo info, + SurfaceControl.Transaction startTransaction, + IRemoteTransitionFinishedCallback finishCallback) + throws RemoteException { + mCalled = true; + finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */); + } + + @Override + public void mergeAnimation(IBinder transition, TransitionInfo info, + SurfaceControl.Transaction t, IBinder mergeTarget, + IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { + } + + @Override + public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException { + mConsumed = true; + } + + /** + * Check whether this remote transition + * {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction, + * IRemoteTransitionFinishedCallback)} is called + */ + public boolean isCalled() { + return mCalled; + } + + /** + * Check whether this remote transition's {@link #onTransitionConsumed(IBinder, boolean)} + * is called + */ + public boolean isConsumed() { + return mConsumed; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TransitionInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TransitionInfoBuilder.java index a658375ca38a..834385832e4a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TransitionInfoBuilder.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TransitionInfoBuilder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell; +package com.android.wm.shell.transition; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java deleted file mode 100644 index 596d6dd3a3d2..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.wm.shell.windowdecor; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.ActivityManager; -import android.app.WindowConfiguration; -import android.graphics.Rect; -import android.hardware.display.DisplayManager; -import android.hardware.display.VirtualDisplay; -import android.hardware.input.InputManager; -import android.os.Handler; -import android.os.Looper; -import android.view.Choreographer; -import android.view.Display; -import android.view.InputChannel; -import android.view.InputMonitor; -import android.view.SurfaceControl; -import android.view.SurfaceView; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.TestRunningTaskInfoBuilder; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.desktopmode.DesktopModeController; -import com.android.wm.shell.desktopmode.DesktopTasksController; -import com.android.wm.shell.sysui.ShellController; -import com.android.wm.shell.sysui.ShellInit; -import com.android.wm.shell.transition.Transitions; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -/** Tests of {@link DesktopModeWindowDecorViewModel} */ -@SmallTest -public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { - - private static final String TAG = "DesktopModeWindowDecorViewModelTests"; - private static final Rect STABLE_INSETS = new Rect(0, 100, 0, 0); - - @Mock private DesktopModeWindowDecoration mDesktopModeWindowDecoration; - @Mock private DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory; - - @Mock private Handler mMainHandler; - @Mock private Choreographer mMainChoreographer; - @Mock private ShellTaskOrganizer mTaskOrganizer; - @Mock private DisplayController mDisplayController; - @Mock private DisplayLayout mDisplayLayout; - @Mock private SyncTransactionQueue mSyncQueue; - @Mock private DesktopModeController mDesktopModeController; - @Mock private DesktopTasksController mDesktopTasksController; - @Mock private InputMonitor mInputMonitor; - @Mock private InputManager mInputManager; - @Mock private Transitions mTransitions; - @Mock private DesktopModeWindowDecorViewModel.InputMonitorFactory mMockInputMonitorFactory; - @Mock private Supplier<SurfaceControl.Transaction> mTransactionFactory; - @Mock private SurfaceControl.Transaction mTransaction; - @Mock private Display mDisplay; - @Mock private ShellController mShellController; - @Mock private ShellInit mShellInit; - @Mock private DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener - mDesktopModeKeyguardChangeListener; - private final List<InputManager> mMockInputManagers = new ArrayList<>(); - - private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel; - - @Before - public void setUp() { - mMockInputManagers.add(mInputManager); - - mDesktopModeWindowDecorViewModel = - new DesktopModeWindowDecorViewModel( - mContext, - mMainHandler, - mMainChoreographer, - mShellInit, - mTaskOrganizer, - mDisplayController, - mShellController, - mSyncQueue, - mTransitions, - Optional.of(mDesktopModeController), - Optional.of(mDesktopTasksController), - mDesktopModeWindowDecorFactory, - mMockInputMonitorFactory, - mTransactionFactory, - mDesktopModeKeyguardChangeListener - ); - - doReturn(mDesktopModeWindowDecoration) - .when(mDesktopModeWindowDecorFactory) - .create(any(), any(), any(), any(), any(), any(), any(), any()); - doReturn(mTransaction).when(mTransactionFactory).get(); - doReturn(mDisplayLayout).when(mDisplayController).getDisplayLayout(anyInt()); - doReturn(STABLE_INSETS).when(mDisplayLayout).stableInsets(); - doNothing().when(mShellController).addKeyguardChangeListener(any()); - - when(mMockInputMonitorFactory.create(any(), any())).thenReturn(mInputMonitor); - // InputChannel cannot be mocked because it passes to InputEventReceiver. - final InputChannel[] inputChannels = InputChannel.openInputChannelPair(TAG); - inputChannels[0].dispose(); - when(mInputMonitor.getInputChannel()).thenReturn(inputChannels[1]); - - mDesktopModeWindowDecoration.mDisplay = mDisplay; - doReturn(Display.DEFAULT_DISPLAY).when(mDisplay).getDisplayId(); - } - - @Test - public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception { - final int taskId = 1; - final ActivityManager.RunningTaskInfo taskInfo = - createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM); - SurfaceControl surfaceControl = mock(SurfaceControl.class); - runOnMainThread(() -> { - final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); - final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); - - mDesktopModeWindowDecorViewModel.onTaskOpening( - taskInfo, surfaceControl, startT, finishT); - - taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED); - taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); - mDesktopModeWindowDecorViewModel.onTaskChanging( - taskInfo, surfaceControl, startT, finishT); - }); - verify(mDesktopModeWindowDecorFactory) - .create( - mContext, - mDisplayController, - mTaskOrganizer, - taskInfo, - surfaceControl, - mMainHandler, - mMainChoreographer, - mSyncQueue); - verify(mDesktopModeWindowDecoration).close(); - } - - @Test - public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception { - final int taskId = 1; - final ActivityManager.RunningTaskInfo taskInfo = - createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_UNDEFINED); - SurfaceControl surfaceControl = mock(SurfaceControl.class); - runOnMainThread(() -> { - final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); - final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); - taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); - - mDesktopModeWindowDecorViewModel.onTaskChanging( - taskInfo, surfaceControl, startT, finishT); - - taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); - taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD); - - mDesktopModeWindowDecorViewModel.onTaskChanging( - taskInfo, surfaceControl, startT, finishT); - }); - verify(mDesktopModeWindowDecorFactory, times(1)) - .create( - mContext, - mDisplayController, - mTaskOrganizer, - taskInfo, - surfaceControl, - mMainHandler, - mMainChoreographer, - mSyncQueue); - } - - @Test - public void testCreateAndDisposeEventReceiver() throws Exception { - final int taskId = 1; - final ActivityManager.RunningTaskInfo taskInfo = - createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM); - taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD); - runOnMainThread(() -> { - SurfaceControl surfaceControl = mock(SurfaceControl.class); - final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); - final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); - - mDesktopModeWindowDecorViewModel.onTaskOpening( - taskInfo, surfaceControl, startT, finishT); - - mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo); - }); - verify(mMockInputMonitorFactory).create(any(), any()); - verify(mInputMonitor).dispose(); - } - - @Test - public void testEventReceiversOnMultipleDisplays() throws Exception { - runOnMainThread(() -> { - SurfaceView surfaceView = new SurfaceView(mContext); - final DisplayManager mDm = mContext.getSystemService(DisplayManager.class); - final VirtualDisplay secondaryDisplay = mDm.createVirtualDisplay( - "testEventReceiversOnMultipleDisplays", /*width=*/ 400, /*height=*/ 400, - /*densityDpi=*/ 320, surfaceView.getHolder().getSurface(), - DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); - try { - int secondaryDisplayId = secondaryDisplay.getDisplay().getDisplayId(); - - final int taskId = 1; - final ActivityManager.RunningTaskInfo taskInfo = - createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM); - final ActivityManager.RunningTaskInfo secondTaskInfo = - createTaskInfo(taskId + 1, secondaryDisplayId, WINDOWING_MODE_FREEFORM); - final ActivityManager.RunningTaskInfo thirdTaskInfo = - createTaskInfo(taskId + 2, secondaryDisplayId, WINDOWING_MODE_FREEFORM); - - SurfaceControl surfaceControl = mock(SurfaceControl.class); - final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); - final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); - - mDesktopModeWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT, - finishT); - mDesktopModeWindowDecorViewModel.onTaskOpening(secondTaskInfo, surfaceControl, - startT, finishT); - mDesktopModeWindowDecorViewModel.onTaskOpening(thirdTaskInfo, surfaceControl, - startT, finishT); - mDesktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTaskInfo); - mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo); - } finally { - secondaryDisplay.release(); - } - }); - verify(mMockInputMonitorFactory, times(2)).create(any(), any()); - verify(mInputMonitor, times(1)).dispose(); - } - - @Test - public void testCaptionIsNotCreatedWhenKeyguardIsVisible() throws Exception { - doReturn(true).when( - mDesktopModeKeyguardChangeListener).isKeyguardVisibleAndOccluded(); - - final int taskId = 1; - final ActivityManager.RunningTaskInfo taskInfo = - createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN); - taskInfo.isFocused = true; - SurfaceControl surfaceControl = mock(SurfaceControl.class); - runOnMainThread(() -> { - final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); - final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); - - mDesktopModeWindowDecorViewModel.onTaskOpening( - taskInfo, surfaceControl, startT, finishT); - - taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED); - taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); - mDesktopModeWindowDecorViewModel.onTaskChanging( - taskInfo, surfaceControl, startT, finishT); - }); - verify(mDesktopModeWindowDecorFactory, never()) - .create(any(), any(), any(), any(), any(), any(), any(), any()); - } - - private void runOnMainThread(Runnable r) throws Exception { - final Handler mainHandler = new Handler(Looper.getMainLooper()); - final CountDownLatch latch = new CountDownLatch(1); - mainHandler.post(() -> { - r.run(); - latch.countDown(); - }); - latch.await(1, TimeUnit.SECONDS); - } - - private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, - int displayId, @WindowConfiguration.WindowingMode int windowingMode) { - ActivityManager.RunningTaskInfo taskInfo = - new TestRunningTaskInfoBuilder() - .setDisplayId(displayId) - .setVisible(true) - .build(); - taskInfo.taskId = taskId; - taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); - return taskInfo; - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt new file mode 100644 index 000000000000..57aa47e85556 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.windowdecor + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED +import android.graphics.Rect +import android.hardware.display.DisplayManager +import android.hardware.display.VirtualDisplay +import android.os.Handler +import android.os.IBinder +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.view.Choreographer +import android.view.Display.DEFAULT_DISPLAY +import android.view.InputChannel +import android.view.InputMonitor +import android.view.InsetsSource +import android.view.InsetsState +import android.view.SurfaceControl +import android.view.SurfaceView +import android.view.WindowInsets.Type.navigationBars +import android.view.WindowInsets.Type.statusBars +import androidx.core.content.getSystemService +import androidx.test.filters.SmallTest +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.TestRunningTaskInfoBuilder +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayInsetsController +import com.android.wm.shell.common.DisplayLayout +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.desktopmode.DesktopTasksController +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener +import com.android.wm.shell.sysui.KeyguardChangeListener +import com.android.wm.shell.sysui.ShellCommandHandler +import com.android.wm.shell.sysui.ShellController +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever +import java.util.Optional +import java.util.function.Supplier + + +/** Tests of [DesktopModeWindowDecorViewModel] */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class DesktopModeWindowDecorViewModelTests : ShellTestCase() { + @Mock private lateinit var mockDesktopModeWindowDecorFactory: + DesktopModeWindowDecoration.Factory + @Mock private lateinit var mockMainHandler: Handler + @Mock private lateinit var mockMainChoreographer: Choreographer + @Mock private lateinit var mockTaskOrganizer: ShellTaskOrganizer + @Mock private lateinit var mockDisplayController: DisplayController + @Mock private lateinit var mockDisplayLayout: DisplayLayout + @Mock private lateinit var displayInsetsController: DisplayInsetsController + @Mock private lateinit var mockSyncQueue: SyncTransactionQueue + @Mock private lateinit var mockDesktopTasksController: DesktopTasksController + @Mock private lateinit var mockInputMonitor: InputMonitor + @Mock private lateinit var mockTransitions: Transitions + @Mock private lateinit var mockInputMonitorFactory: + DesktopModeWindowDecorViewModel.InputMonitorFactory + @Mock private lateinit var mockShellController: ShellController + @Mock private lateinit var mockShellExecutor: ShellExecutor + @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer + @Mock private lateinit var mockRecentsTransitionHandler: RecentsTransitionHandler + @Mock private lateinit var mockShellCommandHandler: ShellCommandHandler + + private val transactionFactory = Supplier<SurfaceControl.Transaction> { + SurfaceControl.Transaction() + } + + private lateinit var shellInit: ShellInit + private lateinit var desktopModeOnInsetsChangedListener: DesktopModeOnInsetsChangedListener + private lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel + + @Before + fun setUp() { + shellInit = ShellInit(mockShellExecutor) + desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel( + mContext, + mockMainHandler, + mockMainChoreographer, + shellInit, + mockShellCommandHandler, + mockTaskOrganizer, + mockDisplayController, + mockShellController, + displayInsetsController, + mockSyncQueue, + mockTransitions, + Optional.of(mockDesktopTasksController), + mockRecentsTransitionHandler, + mockDesktopModeWindowDecorFactory, + mockInputMonitorFactory, + transactionFactory, + mockRootTaskDisplayAreaOrganizer + ) + + whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout) + whenever(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS) + whenever(mockInputMonitorFactory.create(any(), any())).thenReturn(mockInputMonitor) + + // InputChannel cannot be mocked because it passes to InputEventReceiver. + val inputChannels = InputChannel.openInputChannelPair(TAG) + inputChannels.first().dispose() + whenever(mockInputMonitor.inputChannel).thenReturn(inputChannels[1]) + + shellInit.init() + + val listenerCaptor = + argumentCaptor<DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener>() + verify(displayInsetsController).addInsetsChangedListener(anyInt(), listenerCaptor.capture()) + desktopModeOnInsetsChangedListener = listenerCaptor.firstValue + } + + @Test + fun testDeleteCaptionOnChangeTransitionWhenNecessary() { + val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM) + val taskSurface = SurfaceControl() + val decoration = setUpMockDecorationForTask(task) + + onTaskOpening(task, taskSurface) + + task.setWindowingMode(WINDOWING_MODE_UNDEFINED) + task.setActivityType(ACTIVITY_TYPE_UNDEFINED) + onTaskChanging(task, taskSurface) + + verify(mockDesktopModeWindowDecorFactory).create( + mContext, + mockDisplayController, + mockTaskOrganizer, + task, + taskSurface, + mockMainHandler, + mockMainChoreographer, + mockSyncQueue, + mockRootTaskDisplayAreaOrganizer + ) + verify(decoration).close() + } + + @Test + fun testCreateCaptionOnChangeTransitionWhenNecessary() { + val task = createTask( + windowingMode = WINDOWING_MODE_UNDEFINED, + activityType = ACTIVITY_TYPE_UNDEFINED + ) + val taskSurface = SurfaceControl() + setUpMockDecorationForTask(task) + + onTaskChanging(task, taskSurface) + verify(mockDesktopModeWindowDecorFactory, never()).create( + mContext, + mockDisplayController, + mockTaskOrganizer, + task, + taskSurface, + mockMainHandler, + mockMainChoreographer, + mockSyncQueue, + mockRootTaskDisplayAreaOrganizer + ) + + task.setWindowingMode(WINDOWING_MODE_FREEFORM) + task.setActivityType(ACTIVITY_TYPE_STANDARD) + onTaskChanging(task, taskSurface) + verify(mockDesktopModeWindowDecorFactory, times(1)).create( + mContext, + mockDisplayController, + mockTaskOrganizer, + task, + taskSurface, + mockMainHandler, + mockMainChoreographer, + mockSyncQueue, + mockRootTaskDisplayAreaOrganizer + ) + } + + @Test + fun testCreateAndDisposeEventReceiver() { + val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM) + setUpMockDecorationForTask(task) + + onTaskOpening(task) + desktopModeWindowDecorViewModel.destroyWindowDecoration(task) + + verify(mockInputMonitorFactory).create(any(), any()) + verify(mockInputMonitor).dispose() + } + + @Test + fun testEventReceiversOnMultipleDisplays() { + val secondaryDisplay = createVirtualDisplay() ?: return + val secondaryDisplayId = secondaryDisplay.display.displayId + val task = createTask(displayId = DEFAULT_DISPLAY, windowingMode = WINDOWING_MODE_FREEFORM) + val secondTask = createTask( + displayId = secondaryDisplayId, + windowingMode = WINDOWING_MODE_FREEFORM + ) + val thirdTask = createTask( + displayId = secondaryDisplayId, + windowingMode = WINDOWING_MODE_FREEFORM + ) + setUpMockDecorationsForTasks(task, secondTask, thirdTask) + + onTaskOpening(task) + onTaskOpening(secondTask) + onTaskOpening(thirdTask) + desktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTask) + desktopModeWindowDecorViewModel.destroyWindowDecoration(task) + secondaryDisplay.release() + + verify(mockInputMonitorFactory, times(2)).create(any(), any()) + verify(mockInputMonitor, times(1)).dispose() + } + + @Test + fun testCaptionIsNotCreatedWhenKeyguardIsVisible() { + val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true) + val keyguardListenerCaptor = argumentCaptor<KeyguardChangeListener>() + verify(mockShellController).addKeyguardChangeListener(keyguardListenerCaptor.capture()) + + keyguardListenerCaptor.firstValue.onKeyguardVisibilityChanged( + true /* visible */, + true /* occluded */, + false /* animatingDismiss */ + ) + onTaskOpening(task) + + task.setWindowingMode(WINDOWING_MODE_UNDEFINED) + task.setWindowingMode(ACTIVITY_TYPE_UNDEFINED) + onTaskChanging(task) + + verify(mockDesktopModeWindowDecorFactory, never()) + .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any()) + } + + @Test + fun testRelayoutBlockedDuringRecentsTransition() { + val recentsCaptor = argumentCaptor<RecentsTransitionStateListener>() + verify(mockRecentsTransitionHandler).addTransitionStateListener(recentsCaptor.capture()) + + val transition = mock(IBinder::class.java) + val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM) + val decoration = setUpMockDecorationForTask(task) + + // Make sure a window decorations exists first by launching a freeform task. + onTaskOpening(task) + // Now call back when a Recents transition starts. + recentsCaptor.firstValue.onTransitionStarted(transition) + + verify(decoration).incrementRelayoutBlock() + verify(decoration).addTransitionPausingRelayout(transition) + } + + @Test + fun testRelayoutRunsWhenStatusBarsInsetsSourceVisibilityChanges() { + val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM, focused = true) + val decoration = setUpMockDecorationForTask(task) + + onTaskOpening(task) + + // Add status bar insets source + val insetsState = InsetsState() + val statusBarInsetsSourceId = 0 + val statusBarInsetsSource = InsetsSource(statusBarInsetsSourceId, statusBars()) + statusBarInsetsSource.isVisible = false + insetsState.addSource(statusBarInsetsSource) + + desktopModeOnInsetsChangedListener.insetsChanged(insetsState) + + // Verify relayout occurs when status bar inset visibility changes + verify(decoration, times(1)).relayout(task) + } + + @Test + fun testRelayoutDoesNotRunWhenNonStatusBarsInsetsSourceVisibilityChanges() { + val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM, focused = true) + val decoration = setUpMockDecorationForTask(task) + + onTaskOpening(task) + + // Add navigation bar insets source + val insetsState = InsetsState() + val navigationBarInsetsSourceId = 1 + val navigationBarInsetsSource = InsetsSource(navigationBarInsetsSourceId, navigationBars()) + navigationBarInsetsSource.isVisible = false + insetsState.addSource(navigationBarInsetsSource) + + desktopModeOnInsetsChangedListener.insetsChanged(insetsState) + + // Verify relayout does not occur when non-status bar inset changes visibility + verify(decoration, never()).relayout(task) + } + + @Test + fun testRelayoutDoesNotRunWhenNonStatusBarsInsetSourceVisibilityDoesNotChange() { + val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM, focused = true) + val decoration = setUpMockDecorationForTask(task) + + onTaskOpening(task) + + // Add status bar insets source + val insetsState = InsetsState() + val statusBarInsetsSourceId = 0 + val statusBarInsetsSource = InsetsSource(statusBarInsetsSourceId, statusBars()) + statusBarInsetsSource.isVisible = false + insetsState.addSource(statusBarInsetsSource) + + desktopModeOnInsetsChangedListener.insetsChanged(insetsState) + desktopModeOnInsetsChangedListener.insetsChanged(insetsState) + + // Verify relayout runs only once when status bar inset visibility changes. + verify(decoration, times(1)).relayout(task) + } + + private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) { + desktopModeWindowDecorViewModel.onTaskOpening( + task, + leash, + SurfaceControl.Transaction(), + SurfaceControl.Transaction() + ) + } + + private fun onTaskChanging(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) { + desktopModeWindowDecorViewModel.onTaskChanging( + task, + leash, + SurfaceControl.Transaction(), + SurfaceControl.Transaction() + ) + } + + private fun createTask( + displayId: Int = DEFAULT_DISPLAY, + @WindowConfiguration.WindowingMode windowingMode: Int, + activityType: Int = ACTIVITY_TYPE_STANDARD, + focused: Boolean = true + ): RunningTaskInfo { + return TestRunningTaskInfoBuilder() + .setDisplayId(displayId) + .setWindowingMode(windowingMode) + .setVisible(true) + .setActivityType(activityType) + .build().apply { + isFocused = focused + } + } + + private fun setUpMockDecorationForTask(task: RunningTaskInfo): DesktopModeWindowDecoration { + val decoration = mock(DesktopModeWindowDecoration::class.java) + whenever(mockDesktopModeWindowDecorFactory.create( + any(), any(), any(), eq(task), any(), any(), any(), any(), any()) + ).thenReturn(decoration) + decoration.mTaskInfo = task + whenever(decoration.isFocused).thenReturn(task.isFocused) + return decoration + } + + private fun setUpMockDecorationsForTasks(vararg tasks: RunningTaskInfo) { + tasks.forEach { setUpMockDecorationForTask(it) } + } + + private fun createVirtualDisplay(): VirtualDisplay? { + val surfaceView = SurfaceView(mContext) + return mContext.getSystemService<DisplayManager>()?.createVirtualDisplay( + "testEventReceiversOnMultipleDisplays", + /*width=*/ 400, + /*height=*/ 400, + /*densityDpi=*/320, + surfaceView.holder.surface, + DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY + ) + } + + private fun RunningTaskInfo.setWindowingMode(@WindowConfiguration.WindowingMode mode: Int) { + configuration.windowConfiguration.windowingMode = mode + } + + private fun RunningTaskInfo.setActivityType(type: Int) { + configuration.windowConfiguration.activityType = type + } + + companion object { + private const val TAG = "DesktopModeWindowDecorViewModelTests" + private val STABLE_INSETS = Rect(0, 100, 0, 0) + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java new file mode 100644 index 000000000000..a2dbab197fb5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.windowdecor; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.res.Configuration; +import android.os.Handler; +import android.testing.AndroidTestingRunner; +import android.view.Choreographer; +import android.view.Display; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.window.WindowContainerTransaction; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.TestRunningTaskInfoBuilder; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.SyncTransactionQueue; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.util.function.Supplier; + +/** + * Tests for {@link DesktopModeWindowDecoration}. + * + * Build/Install/Run: + * atest WMShellUnitTests:DesktopModeWindowDecorationTests + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class DesktopModeWindowDecorationTests extends ShellTestCase { + @Mock + private DisplayController mMockDisplayController; + @Mock + private ShellTaskOrganizer mMockShellTaskOrganizer; + @Mock + private Handler mMockHandler; + @Mock + private Choreographer mMockChoreographer; + @Mock + private SyncTransactionQueue mMockSyncQueue; + @Mock + private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer; + @Mock + private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier; + @Mock + private SurfaceControl.Transaction mMockTransaction; + @Mock + private SurfaceControl mMockSurfaceControl; + @Mock + private SurfaceControlViewHost mMockSurfaceControlViewHost; + @Mock + private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory; + + private final Configuration mConfiguration = new Configuration(); + + @Before + public void setUp() { + doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create( + any(), any(), any()); + doReturn(mMockTransaction).when(mMockTransactionSupplier).get(); + } + + @Test + public void testMenusClosedWhenTaskIsInvisible() { + doReturn(mMockTransaction).when(mMockTransaction).hide(any()); + + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(false /* visible */); + final DesktopModeWindowDecoration spyWindowDecor = + spy(createWindowDecoration(taskInfo)); + + spyWindowDecor.relayout(taskInfo); + + // Menus should close if open before the task being invisible causes relayout to return. + verify(spyWindowDecor).closeHandleMenu(); + verify(spyWindowDecor).closeMaximizeMenu(); + + } + + private DesktopModeWindowDecoration createWindowDecoration( + ActivityManager.RunningTaskInfo taskInfo) { + return new DesktopModeWindowDecoration(mContext, mMockDisplayController, + mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mConfiguration, + mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer, + SurfaceControl.Builder::new, mMockTransactionSupplier, + WindowContainerTransaction::new, mMockSurfaceControlViewHostFactory); + } + + private ActivityManager.RunningTaskInfo createTaskInfo(boolean visible) { + final ActivityManager.TaskDescription.Builder taskDescriptionBuilder = + new ActivityManager.TaskDescription.Builder(); + ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setTaskDescriptionBuilder(taskDescriptionBuilder) + .setVisible(visible) + .build(); + taskInfo.realActivity = new ComponentName("com.android.wm.shell.windowdecor", + "DesktopModeWindowDecorationTests"); + taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor", + "DesktopModeWindowDecorationTests"); + return taskInfo; + + } +} 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 de46b31879ed..5c0e04aecf6c 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 @@ -76,7 +76,7 @@ class DragPositioningCallbackUtilityTest { minHeight = MIN_HEIGHT defaultMinSize = DEFAULT_MIN displayId = DISPLAY_ID - configuration.windowConfiguration.bounds = STARTING_BOUNDS + configuration.windowConfiguration.setBounds(STARTING_BOUNDS) } mockWindowDecoration.mDisplay = mockDisplay whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt index 6f0599aa8243..add78b2ee8b3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt @@ -6,6 +6,9 @@ import android.graphics.Rect import android.os.IBinder import android.testing.AndroidTestingRunner import android.view.Display +import android.view.Surface +import android.view.Surface.ROTATION_270 +import android.view.Surface.ROTATION_90 import android.view.SurfaceControl import android.window.WindowContainerToken import android.window.WindowContainerTransaction @@ -24,6 +27,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.eq import org.mockito.Mock +import org.mockito.Mockito import org.mockito.Mockito.any import org.mockito.Mockito.argThat import org.mockito.Mockito.never @@ -76,7 +80,15 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout) whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI) whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> - (i.arguments.first() as Rect).set(STABLE_BOUNDS) + if (mockWindowDecoration.mTaskInfo.configuration.windowConfiguration + .displayRotation == ROTATION_90 || + mockWindowDecoration.mTaskInfo.configuration.windowConfiguration + .displayRotation == ROTATION_270 + ) { + (i.arguments.first() as Rect).set(STABLE_BOUNDS_LANDSCAPE) + } else { + (i.arguments.first() as Rect).set(STABLE_BOUNDS_PORTRAIT) + } } `when`(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS) `when`(mockTransactionFactory.get()).thenReturn(mockTransaction) @@ -88,7 +100,8 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { minHeight = MIN_HEIGHT defaultMinSize = DEFAULT_MIN displayId = DISPLAY_ID - configuration.windowConfiguration.bounds = STARTING_BOUNDS + configuration.windowConfiguration.setBounds(STARTING_BOUNDS) + configuration.windowConfiguration.displayRotation = ROTATION_90 } mockWindowDecoration.mDisplay = mockDisplay whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID } @@ -623,7 +636,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { ) val newX = STARTING_BOUNDS.left.toFloat() - val newY = STABLE_BOUNDS.top.toFloat() - 5 + val newY = STABLE_BOUNDS_LANDSCAPE.top.toFloat() - 5 taskPositioner.onDragPositioningMove( newX, newY @@ -641,9 +654,81 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { token == taskBinder && (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && change.configuration.windowConfiguration.bounds.top == - STABLE_BOUNDS.top + STABLE_BOUNDS_LANDSCAPE.top + } + }) + } + + @Test + fun testDragResize_drag_updatesStableBoundsOnRotate() { + // Test landscape stable bounds + performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(), + STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000, + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM) + val rectAfterDrag = Rect(STARTING_BOUNDS) + rectAfterDrag.right += 2000 + // First drag; we should fetch stable bounds. + verify(mockDisplayLayout, Mockito.times(1)).getStableBounds(any()) + verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && + change.configuration.windowConfiguration.bounds == rectAfterDrag + } + }) + // Drag back to starting bounds. + performDrag( + STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat(), + STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(), + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM) + + // Display did not rotate; we should use previous stable bounds + verify(mockDisplayLayout, Mockito.times(1)).getStableBounds(any()) + + // Rotate the screen to portrait + mockWindowDecoration.mTaskInfo.apply { + configuration.windowConfiguration.displayRotation = Surface.ROTATION_0 + } + // Test portrait stable bounds + performDrag( + STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(), + STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000, + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM) + rectAfterDrag.right -= 2000 + rectAfterDrag.bottom += 2000 + + verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && + change.configuration.windowConfiguration.bounds == rectAfterDrag } }) + // Display has rotated; we expect a new stable bounds. + verify(mockDisplayLayout, Mockito.times(2)).getStableBounds(any()) + } + + private fun performDrag( + startX: Float, + startY: Float, + endX: Float, + endY: Float, + ctrlType: Int + ) { + taskPositioner.onDragPositioningStart( + ctrlType, + startX, + startY + ) + taskPositioner.onDragPositioningMove( + endX, + endY + ) + + taskPositioner.onDragPositioningEnd( + endX, + endY + ) } companion object { @@ -664,11 +749,17 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT, DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom) - private val STABLE_BOUNDS = Rect( + private val STABLE_BOUNDS_LANDSCAPE = Rect( DISPLAY_BOUNDS.left, DISPLAY_BOUNDS.top + CAPTION_HEIGHT, DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT ) + private val STABLE_BOUNDS_PORTRAIT = Rect( + DISPLAY_BOUNDS.top, + DISPLAY_BOUNDS.left + CAPTION_HEIGHT, + DISPLAY_BOUNDS.bottom, + DISPLAY_BOUNDS.right - NAVBAR_HEIGHT + ) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt index 3465ddd9d101..a70ebf14324a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt @@ -21,6 +21,9 @@ import android.graphics.Rect import android.os.IBinder import android.testing.AndroidTestingRunner import android.view.Display +import android.view.Surface.ROTATION_0 +import android.view.Surface.ROTATION_270 +import android.view.Surface.ROTATION_90 import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE import android.window.WindowContainerToken @@ -30,6 +33,7 @@ import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED @@ -93,10 +97,17 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout) whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI) whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> - (i.arguments.first() as Rect).set(STABLE_BOUNDS) + if (mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration + .displayRotation == ROTATION_90 || + mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration + .displayRotation == ROTATION_270 + ) { + (i.arguments.first() as Rect).set(STABLE_BOUNDS_LANDSCAPE) + } else { + (i.arguments.first() as Rect).set(STABLE_BOUNDS_PORTRAIT) + } } `when`(mockTransactionFactory.get()).thenReturn(mockTransaction) - mockDesktopWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { taskId = TASK_ID token = taskToken @@ -104,7 +115,8 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { minHeight = MIN_HEIGHT defaultMinSize = DEFAULT_MIN displayId = DISPLAY_ID - configuration.windowConfiguration.bounds = STARTING_BOUNDS + configuration.windowConfiguration.setBounds(STARTING_BOUNDS) + configuration.windowConfiguration.displayRotation = ROTATION_90 } mockDesktopWindowDecoration.mDisplay = mockDisplay whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID } @@ -343,7 +355,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { ) val newX = STARTING_BOUNDS.left.toFloat() - val newY = STABLE_BOUNDS.top.toFloat() - 5 + val newY = STABLE_BOUNDS_LANDSCAPE.top.toFloat() - 5 taskPositioner.onDragPositioningMove( newX, newY @@ -361,11 +373,79 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { token == taskBinder && (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && change.configuration.windowConfiguration.bounds.top == - STABLE_BOUNDS.top + STABLE_BOUNDS_LANDSCAPE.top } }) } + @Test + fun testDragResize_drag_updatesStableBoundsOnRotate() { + // Test landscape stable bounds + performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(), + STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000, + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM) + val rectAfterDrag = Rect(STARTING_BOUNDS) + rectAfterDrag.right += 2000 + // First drag; we should fetch stable bounds. + verify(mockDisplayLayout, times(1)).getStableBounds(any()) + verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && + change.configuration.windowConfiguration.bounds == rectAfterDrag}}, + eq(taskPositioner)) + // Drag back to starting bounds. + performDrag(STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat(), + STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(), + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM) + + // Display did not rotate; we should use previous stable bounds + verify(mockDisplayLayout, times(1)).getStableBounds(any()) + + // Rotate the screen to portrait + mockDesktopWindowDecoration.mTaskInfo.apply { + configuration.windowConfiguration.displayRotation = ROTATION_0 + } + // Test portrait stable bounds + performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(), + STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000, + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM) + rectAfterDrag.right -= 2000 + rectAfterDrag.bottom += 2000 + + verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && + change.configuration.windowConfiguration.bounds == rectAfterDrag}}, + eq(taskPositioner)) + // Display has rotated; we expect a new stable bounds. + verify(mockDisplayLayout, times(2)).getStableBounds(any()) + } + + private fun performDrag( + startX: Float, + startY: Float, + endX: Float, + endY: Float, + ctrlType: Int + ) { + taskPositioner.onDragPositioningStart( + ctrlType, + startX, + startY + ) + taskPositioner.onDragPositioningMove( + endX, + endY + ) + + taskPositioner.onDragPositioningEnd( + endX, + endY + ) + } + companion object { private const val TASK_ID = 5 private const val MIN_WIDTH = 10 @@ -378,11 +458,17 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { private const val DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT = 10 private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600) private val STARTING_BOUNDS = Rect(100, 100, 200, 200) - private val STABLE_BOUNDS = Rect( + private val STABLE_BOUNDS_LANDSCAPE = Rect( DISPLAY_BOUNDS.left, DISPLAY_BOUNDS.top + CAPTION_HEIGHT, DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT ) + private val STABLE_BOUNDS_PORTRAIT = Rect( + DISPLAY_BOUNDS.top, + DISPLAY_BOUNDS.left + CAPTION_HEIGHT, + DISPLAY_BOUNDS.bottom, + DISPLAY_BOUNDS.right - NAVBAR_HEIGHT + ) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index 7fc1c99bb44e..8061aa3f844a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -17,12 +17,19 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.WindowInsets.Type.captionBar; +import static android.view.WindowInsets.Type.mandatorySystemGestures; +import static android.view.WindowInsets.Type.statusBars; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlBuilder; import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction; import static com.google.common.truth.Truth.assertThat; +import static junit.framework.Assert.assertTrue; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyInt; @@ -36,9 +43,11 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.quality.Strictness.LENIENT; import android.app.ActivityManager; import android.content.Context; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Point; @@ -47,6 +56,7 @@ import android.testing.AndroidTestingRunner; import android.util.DisplayMetrics; import android.view.AttachedSurfaceControl; import android.view.Display; +import android.view.InsetsState; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.View; @@ -58,10 +68,12 @@ import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.tests.R; import org.junit.Before; @@ -88,6 +100,7 @@ public class WindowDecorationTests extends ShellTestCase { private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400); private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60); private static final int CORNER_RADIUS = 20; + private static final int STATUS_BAR_INSET_SOURCE_ID = 0; private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult = new WindowDecoration.RelayoutResult<>(); @@ -112,13 +125,13 @@ public class WindowDecorationTests extends ShellTestCase { private final List<SurfaceControl.Transaction> mMockSurfaceControlTransactions = new ArrayList<>(); private final List<SurfaceControl.Builder> mMockSurfaceControlBuilders = new ArrayList<>(); + private final InsetsState mInsetsState = new InsetsState(); private SurfaceControl.Transaction mMockSurfaceControlStartT; private SurfaceControl.Transaction mMockSurfaceControlFinishT; private SurfaceControl.Transaction mMockSurfaceControlAddWindowT; private WindowDecoration.RelayoutParams mRelayoutParams = new WindowDecoration.RelayoutParams(); + private Configuration mWindowConfiguration = new Configuration(); private int mCaptionMenuWidthId; - private int mCaptionMenuShadowRadiusId; - private int mCaptionMenuCornerRadiusId; @Before public void setUp() { @@ -129,8 +142,6 @@ public class WindowDecorationTests extends ShellTestCase { mRelayoutParams.mLayoutResId = 0; mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height; mCaptionMenuWidthId = R.dimen.test_freeform_decor_caption_menu_width; - mCaptionMenuShadowRadiusId = R.dimen.test_caption_menu_shadow_radius; - mCaptionMenuCornerRadiusId = R.dimen.test_caption_menu_corner_radius; mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius; mRelayoutParams.mCornerRadius = CORNER_RADIUS; @@ -138,6 +149,11 @@ public class WindowDecorationTests extends ShellTestCase { .create(any(), any(), any()); when(mMockSurfaceControlViewHost.getRootSurfaceControl()) .thenReturn(mMockRootSurfaceControl); + when(mMockView.findViewById(anyInt())).thenReturn(mMockView); + + // Add status bar inset so that WindowDecoration does not think task is in immersive mode + mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, statusBars()).setVisible(true); + doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt()); } @Test @@ -203,12 +219,8 @@ public class WindowDecorationTests extends ShellTestCase { createMockSurfaceControlBuilder(captionContainerSurface); mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); - final ActivityManager.TaskDescription.Builder taskDescriptionBuilder = - new ActivityManager.TaskDescription.Builder() - .setBackgroundColor(Color.YELLOW); final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() .setDisplayId(Display.DEFAULT_DISPLAY) - .setTaskDescriptionBuilder(taskDescriptionBuilder) .setBounds(TASK_BOUNDS) .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y) .setVisible(true) @@ -257,8 +269,6 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockSurfaceControlFinishT).setCornerRadius(taskSurface, CORNER_RADIUS); verify(mMockSurfaceControlStartT) .show(taskSurface); - verify(mMockSurfaceControlStartT) - .setColor(taskSurface, new float[] {1.f, 1.f, 0.f}); verify(mMockSurfaceControlStartT).setShadowRadius(taskSurface, 10); assertEquals(300, mRelayoutResult.mWidth); @@ -296,6 +306,7 @@ public class WindowDecorationTests extends ShellTestCase { taskInfo.isFocused = true; // Density is 2. Shadow radius is 10px. Caption height is 64px. taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2; + mWindowConfiguration.densityDpi = taskInfo.configuration.densityDpi; final SurfaceControl taskSurface = mock(SurfaceControl.class); final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface); @@ -414,16 +425,6 @@ public class WindowDecorationTests extends ShellTestCase { final int height = WindowDecoration.loadDimensionPixelSize( windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId); verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, width, height); - final int shadowRadius = WindowDecoration.loadDimensionPixelSize( - windowDecor.mDecorWindowContext.getResources(), - mCaptionMenuShadowRadiusId); - verify(mMockSurfaceControlAddWindowT) - .setShadowRadius(additionalWindowSurface, shadowRadius); - final int cornerRadius = WindowDecoration.loadDimensionPixelSize( - windowDecor.mDecorWindowContext.getResources(), - mCaptionMenuCornerRadiusId); - verify(mMockSurfaceControlAddWindowT) - .setCornerRadius(additionalWindowSurface, cornerRadius); verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface); verify(mMockSurfaceControlViewHostFactory, Mockito.times(2)) .create(any(), eq(defaultDisplay), any()); @@ -513,10 +514,146 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT); } + @Test + public void testRelayout_fluidResizeEnabled_freeformTask_setTaskSurfaceColor() { + StaticMockitoSession mockitoSession = mockitoSession().mockStatic( + DesktopModeStatus.class).strictness( + LENIENT).startMocking(); + when(DesktopModeStatus.isVeiledResizeEnabled()).thenReturn(false); + + final Display defaultDisplay = mock(Display.class); + doReturn(defaultDisplay).when(mMockDisplayController) + .getDisplay(Display.DEFAULT_DISPLAY); + + final SurfaceControl decorContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder decorContainerSurfaceBuilder = + createMockSurfaceControlBuilder(decorContainerSurface); + mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); + final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder captionContainerSurfaceBuilder = + createMockSurfaceControlBuilder(captionContainerSurface); + mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); + + final ActivityManager.TaskDescription.Builder taskDescriptionBuilder = + new ActivityManager.TaskDescription.Builder() + .setBackgroundColor(Color.YELLOW); + + final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setTaskDescriptionBuilder(taskDescriptionBuilder) + .setVisible(true) + .setWindowingMode(WINDOWING_MODE_FREEFORM) + .build(); + taskInfo.isFocused = true; + final SurfaceControl taskSurface = mock(SurfaceControl.class); + final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface); + + windowDecor.relayout(taskInfo); + + verify(mMockSurfaceControlStartT).setColor(taskSurface, new float[]{1.f, 1.f, 0.f}); + + mockitoSession.finishMocking(); + } + + @Test + public void testInsetsAddedWhenCaptionIsVisible() { + final Display defaultDisplay = mock(Display.class); + doReturn(defaultDisplay).when(mMockDisplayController) + .getDisplay(Display.DEFAULT_DISPLAY); + + final ActivityManager.TaskDescription.Builder taskDescriptionBuilder = + new ActivityManager.TaskDescription.Builder(); + final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setTaskDescriptionBuilder(taskDescriptionBuilder) + .setVisible(true) + .build(); + final SurfaceControl taskSurface = mock(SurfaceControl.class); + final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface); + + assertTrue(mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, statusBars()) + .isVisible()); + assertTrue(mInsetsState.sourceSize() == 1); + assertTrue(mInsetsState.sourceAt(0).getType() == statusBars()); + + windowDecor.relayout(taskInfo); + + verify(mMockWindowContainerTransaction).addInsetsSource(eq(taskInfo.token), any(), + eq(0) /* index */, eq(captionBar()), any()); + verify(mMockWindowContainerTransaction).addInsetsSource(eq(taskInfo.token), any(), + eq(0) /* index */, eq(mandatorySystemGestures()), any()); + } + + @Test + public void testRelayout_fluidResizeEnabled_fullscreenTask_clearTaskSurfaceColor() { + StaticMockitoSession mockitoSession = mockitoSession().mockStatic( + DesktopModeStatus.class).strictness(LENIENT).startMocking(); + when(DesktopModeStatus.isVeiledResizeEnabled()).thenReturn(false); + + final Display defaultDisplay = mock(Display.class); + doReturn(defaultDisplay).when(mMockDisplayController) + .getDisplay(Display.DEFAULT_DISPLAY); + + final SurfaceControl decorContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder decorContainerSurfaceBuilder = + createMockSurfaceControlBuilder(decorContainerSurface); + mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); + final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder captionContainerSurfaceBuilder = + createMockSurfaceControlBuilder(captionContainerSurface); + mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); + + final ActivityManager.TaskDescription.Builder taskDescriptionBuilder = + new ActivityManager.TaskDescription.Builder() + .setBackgroundColor(Color.YELLOW); + final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setTaskDescriptionBuilder(taskDescriptionBuilder) + .setVisible(true) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .build(); + taskInfo.isFocused = true; + final SurfaceControl taskSurface = mock(SurfaceControl.class); + final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface); + + windowDecor.relayout(taskInfo); + + verify(mMockSurfaceControlStartT).unsetColor(taskSurface); + + mockitoSession.finishMocking(); + } + + + @Test + public void testInsetsRemovedWhenCaptionIsHidden() { + final Display defaultDisplay = mock(Display.class); + doReturn(defaultDisplay).when(mMockDisplayController) + .getDisplay(Display.DEFAULT_DISPLAY); + + mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(false); + + final ActivityManager.TaskDescription.Builder taskDescriptionBuilder = + new ActivityManager.TaskDescription.Builder(); + final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setTaskDescriptionBuilder(taskDescriptionBuilder) + .setVisible(true) + .build(); + final SurfaceControl taskSurface = mock(SurfaceControl.class); + final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface); + + windowDecor.relayout(taskInfo); + + verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(), + eq(0) /* index */, eq(captionBar())); + verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(), + eq(0) /* index */, eq(mandatorySystemGestures())); + } + private TestWindowDecoration createWindowDecoration( ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) { return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer, - taskInfo, testSurface, + taskInfo, testSurface, mWindowConfiguration, new MockObjectSupplier<>(mMockSurfaceControlBuilders, () -> createMockSurfaceControlBuilder(mock(SurfaceControl.class))), new MockObjectSupplier<>(mMockSurfaceControlTransactions, @@ -556,13 +693,15 @@ public class WindowDecorationTests extends ShellTestCase { TestWindowDecoration(Context context, DisplayController displayController, ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, + Configuration windowConfiguration, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, SurfaceControlViewHostFactory surfaceControlViewHostFactory) { super(context, displayController, taskOrganizer, taskInfo, taskSurface, - surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, - windowContainerTransactionSupplier, surfaceControlViewHostFactory); + windowConfiguration, surfaceControlBuilderSupplier, + surfaceControlTransactionSupplier, windowContainerTransactionSupplier, + surfaceControlViewHostFactory); } @Override @@ -583,13 +722,11 @@ public class WindowDecorationTests extends ShellTestCase { int y = mRelayoutParams.mCaptionY; int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId); int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId); - int shadowRadius = loadDimensionPixelSize(resources, mCaptionMenuShadowRadiusId); - int cornerRadius = loadDimensionPixelSize(resources, mCaptionMenuCornerRadiusId); String name = "Test Window"; WindowDecoration.AdditionalWindow additionalWindow = - addWindow(R.layout.desktop_mode_window_decor_handle_menu_app_info_pill, name, + addWindow(R.layout.desktop_mode_window_decor_handle_menu, name, mMockSurfaceControlAddWindowT, mMockSurfaceSyncGroup, x, y, - width, height, shadowRadius, cornerRadius); + width, height); return additionalWindow; } } diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 4fb80ac13bbf..47a7f3579764 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -257,6 +257,7 @@ cc_benchmark { "tests/AssetManager2_bench.cpp", "tests/AttributeResolution_bench.cpp", "tests/CursorWindow_bench.cpp", + "tests/Generic_bench.cpp", "tests/SparseEntry_bench.cpp", "tests/Theme_bench.cpp", ], diff --git a/libs/androidfw/ApkParsing.cpp b/libs/androidfw/ApkParsing.cpp index 32d2c5b05acb..7eedfdb5c921 100644 --- a/libs/androidfw/ApkParsing.cpp +++ b/libs/androidfw/ApkParsing.cpp @@ -56,6 +56,11 @@ const char* ValidLibraryPathLastSlash(const char* fileName, bool suppress64Bit, return nullptr; } + // Make sure file starts with 'lib/' prefix. + if (strncmp(fileName, APK_LIB.data(), APK_LIB_LEN) != 0) { + return nullptr; + } + // Make sure there aren't subdirectories by checking if the next / after lib/ is the last slash if (memchr(fileName + APK_LIB_LEN, '/', fileNameLen - APK_LIB_LEN) != lastSlash) { return nullptr; diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 5e04bfea2bef..d056248273b2 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -97,12 +97,17 @@ struct Theme::Entry { Res_value value; }; -AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration) - : configuration_(configuration) { +AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration) { + configurations_.push_back(configuration); + // Don't invalidate caches here as there's nothing cached yet. SetApkAssets(apk_assets, false); } +AssetManager2::AssetManager2() { + configurations_.resize(1); +} + bool AssetManager2::SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches) { BuildDynamicRefTable(apk_assets); RebuildFilterList(); @@ -266,7 +271,7 @@ void AssetManager2::DumpToLog() const { auto op = StartOperation(); std::string list; - for (size_t i = 0; i < apk_assets_.size(); ++i) { + for (size_t i = 0, s = apk_assets_.size(); i < s; ++i) { const auto& assets = GetApkAssets(i); base::StringAppendF(&list, "%s,", assets ? assets->GetDebugName().c_str() : "nullptr"); } @@ -359,7 +364,7 @@ bool AssetManager2::GetOverlayablesToString(android::StringPiece package_name, std::string* out) const { auto op = StartOperation(); uint8_t package_id = 0U; - for (size_t i = 0; i != apk_assets_.size(); ++i) { + for (size_t i = 0, s = apk_assets_.size(); i != s; ++i) { const auto& assets = GetApkAssets(i); if (!assets) { continue; @@ -418,7 +423,7 @@ bool AssetManager2::GetOverlayablesToString(android::StringPiece package_name, bool AssetManager2::ContainsAllocatedTable() const { auto op = StartOperation(); - for (size_t i = 0; i != apk_assets_.size(); ++i) { + for (size_t i = 0, s = apk_assets_.size(); i != s; ++i) { const auto& assets = GetApkAssets(i); if (assets && assets->IsTableAllocated()) { return true; @@ -427,9 +432,16 @@ bool AssetManager2::ContainsAllocatedTable() const { return false; } -void AssetManager2::SetConfiguration(const ResTable_config& configuration) { - const int diff = configuration_.diff(configuration); - configuration_ = configuration; +void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations) { + int diff = 0; + if (configurations_.size() != configurations.size()) { + diff = -1; + } else { + for (int i = 0; i < configurations_.size(); i++) { + diff |= configurations_[i].diff(configurations[i]); + } + } + configurations_ = std::move(configurations); if (diff) { RebuildFilterList(); @@ -626,16 +638,6 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( auto op = StartOperation(); - // Might use this if density_override != 0. - ResTable_config density_override_config; - - // Select our configuration or generate a density override configuration. - const ResTable_config* desired_config = &configuration_; - if (density_override != 0 && density_override != configuration_.density) { - density_override_config = configuration_; - density_override_config.density = density_override; - desired_config = &density_override_config; - } // Retrieve the package group from the package id of the resource id. if (UNLIKELY(!is_valid_resid(resid))) { @@ -654,119 +656,160 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( } const PackageGroup& package_group = package_groups_[package_idx]; - auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, - stop_at_first_match, ignore_configuration); - if (UNLIKELY(!result.has_value())) { - return base::unexpected(result.error()); - } + std::optional<FindEntryResult> final_result; + bool final_has_locale = false; + bool final_overlaid = false; + for (auto & config : configurations_) { + // Might use this if density_override != 0. + ResTable_config density_override_config; + + // Select our configuration or generate a density override configuration. + const ResTable_config* desired_config = &config; + if (density_override != 0 && density_override != config.density) { + density_override_config = config; + density_override_config.density = density_override; + desired_config = &density_override_config; + } - bool overlaid = false; - if (!stop_at_first_match && !ignore_configuration) { - const auto& assets = GetApkAssets(result->cookie); - if (!assets) { - ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid); - return base::unexpected(std::nullopt); + auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, + stop_at_first_match, ignore_configuration); + if (UNLIKELY(!result.has_value())) { + return base::unexpected(result.error()); } - if (!assets->IsLoader()) { - for (const auto& id_map : package_group.overlays_) { - auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid); - if (!overlay_entry) { - // No id map entry exists for this target resource. - continue; - } - if (overlay_entry.IsInlineValue()) { - // The target resource is overlaid by an inline value not represented by a resource. - ConfigDescription best_frro_config; - Res_value best_frro_value; - bool frro_found = false; - for( const auto& [config, value] : overlay_entry.GetInlineValue()) { - if ((!frro_found || config.isBetterThan(best_frro_config, desired_config)) - && config.match(*desired_config)) { - frro_found = true; - best_frro_config = config; - best_frro_value = value; + bool overlaid = false; + if (!stop_at_first_match && !ignore_configuration) { + const auto& assets = GetApkAssets(result->cookie); + if (!assets) { + ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid); + return base::unexpected(std::nullopt); + } + if (!assets->IsLoader()) { + for (const auto& id_map : package_group.overlays_) { + auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid); + if (!overlay_entry) { + // No id map entry exists for this target resource. + continue; + } + if (overlay_entry.IsInlineValue()) { + // The target resource is overlaid by an inline value not represented by a resource. + ConfigDescription best_frro_config; + Res_value best_frro_value; + bool frro_found = false; + for( const auto& [config, value] : overlay_entry.GetInlineValue()) { + if ((!frro_found || config.isBetterThan(best_frro_config, desired_config)) + && config.match(*desired_config)) { + frro_found = true; + best_frro_config = config; + best_frro_value = value; + } } + if (!frro_found) { + continue; + } + result->entry = best_frro_value; + result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + result->cookie = id_map.cookie; + + if (UNLIKELY(logging_enabled)) { + last_resolution_.steps.push_back(Resolution::Step{ + Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()}); + if (auto path = assets->GetPath()) { + const std::string overlay_path = path->data(); + if (IsFabricatedOverlay(overlay_path)) { + // FRRO don't have package name so we use the creating package here. + String8 frro_name = String8("FRRO"); + // Get the first part of it since the expected one should be like + // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro + // under /data/resource-cache/. + const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1); + const size_t end = name.find('-'); + if (frro_name.size() != overlay_path.size() && end != std::string::npos) { + frro_name.append(base::StringPrintf(" created by %s", + name.substr(0 /* pos */, + end).c_str()).c_str()); + } + last_resolution_.best_package_name = frro_name; + } else { + last_resolution_.best_package_name = result->package_name->c_str(); + } + } + overlaid = true; + } + continue; } - if (!frro_found) { + + auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override, + false /* stop_at_first_match */, + false /* ignore_configuration */); + if (UNLIKELY(IsIOError(overlay_result))) { + return base::unexpected(overlay_result.error()); + } + if (!overlay_result.has_value()) { + continue; + } + + if (!overlay_result->config.isBetterThan(result->config, desired_config) + && overlay_result->config.compare(result->config) != 0) { + // The configuration of the entry for the overlay must be equal to or better than the + // target configuration to be chosen as the better value. continue; } - result->entry = best_frro_value; + + result->cookie = overlay_result->cookie; + result->entry = overlay_result->entry; + result->config = overlay_result->config; result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); - result->cookie = id_map.cookie; if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back( - Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, String8(), result->cookie}); - if (auto path = assets->GetPath()) { - const std::string overlay_path = path->data(); - if (IsFabricatedOverlay(overlay_path)) { - // FRRO don't have package name so we use the creating package here. - String8 frro_name = String8("FRRO"); - // Get the first part of it since the expected one should be like - // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro - // under /data/resource-cache/. - const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1); - const size_t end = name.find('-'); - if (frro_name.size() != overlay_path.size() && end != std::string::npos) { - frro_name.append(base::StringPrintf(" created by %s", - name.substr(0 /* pos */, - end).c_str()).c_str()); - } - last_resolution_.best_package_name = frro_name; - } else { - last_resolution_.best_package_name = result->package_name->c_str(); - } - } + Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->cookie, + overlay_result->config.toString()}); + last_resolution_.best_package_name = + overlay_result->package_name->c_str(); overlaid = true; } - continue; - } - - auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override, - false /* stop_at_first_match */, - false /* ignore_configuration */); - if (UNLIKELY(IsIOError(overlay_result))) { - return base::unexpected(overlay_result.error()); - } - if (!overlay_result.has_value()) { - continue; } + } + } - if (!overlay_result->config.isBetterThan(result->config, desired_config) - && overlay_result->config.compare(result->config) != 0) { - // The configuration of the entry for the overlay must be equal to or better than the target - // configuration to be chosen as the better value. - continue; - } - - result->cookie = overlay_result->cookie; - result->entry = overlay_result->entry; - result->config = overlay_result->config; - result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); - - if (UNLIKELY(logging_enabled)) { - last_resolution_.steps.push_back( - Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(), - overlay_result->cookie}); - last_resolution_.best_package_name = - overlay_result->package_name->c_str(); - overlaid = true; - } + bool has_locale = false; + if (result->config.locale == 0) { + if (default_locale_ != 0) { + ResTable_config conf; + conf.locale = default_locale_; + // Since we know conf has a locale and only a locale, match will tell us if that locale + // matches + has_locale = conf.match(config); } + } else { + has_locale = true; + } + + // if we don't have a result yet + if (!final_result || + // or this config is better before the locale than the existing result + result->config.isBetterThanBeforeLocale(final_result->config, desired_config) || + // or the existing config isn't better before locale and this one specifies a locale + // whereas the existing one doesn't + (!final_result->config.isBetterThanBeforeLocale(result->config, desired_config) + && has_locale && !final_has_locale)) { + final_result = result.value(); + final_overlaid = overlaid; + final_has_locale = has_locale; } } if (UNLIKELY(logging_enabled)) { - last_resolution_.cookie = result->cookie; - last_resolution_.type_string_ref = result->type_string_ref; - last_resolution_.entry_string_ref = result->entry_string_ref; - last_resolution_.best_config_name = result->config.toString(); - if (!overlaid) { - last_resolution_.best_package_name = result->package_name->c_str(); + last_resolution_.cookie = final_result->cookie; + last_resolution_.type_string_ref = final_result->type_string_ref; + last_resolution_.entry_string_ref = final_result->entry_string_ref; + last_resolution_.best_config_name = final_result->config.toString(); + if (!final_overlaid) { + last_resolution_.best_package_name = final_result->package_name->c_str(); } } - return result; + return *final_result; } base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( @@ -784,8 +827,10 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( // If `desired_config` is not the same as the set configuration or the caller will accept a value // from any configuration, then we cannot use our filtered list of types since it only it contains // types matched to the set configuration. - const bool use_filtered = !ignore_configuration && &desired_config == &configuration_; - + const bool use_filtered = !ignore_configuration && std::find_if( + configurations_.begin(), configurations_.end(), + [&desired_config](auto& value) { return &desired_config == &value; }) + != configurations_.end(); const size_t package_count = package_group.packages_.size(); for (size_t pi = 0; pi < package_count; pi++) { const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; @@ -818,9 +863,12 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( // We can skip calling ResTable_config::match() if the caller does not care for the // configuration to match or if we're using the list of types that have already had their - // configuration matched. + // configuration matched. The exception to this is when the user has multiple locales set + // because the filtered list will then have values from multiple locales and we will need to + // call match() to make sure the current entry matches the config we are currently checking. const ResTable_config& this_config = type_entry->config; - if (!(use_filtered || ignore_configuration || this_config.match(desired_config))) { + if (!((use_filtered && (configurations_.size() == 1)) + || ignore_configuration || this_config.match(desired_config))) { continue; } @@ -834,8 +882,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( } else { if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED, - this_config.toString(), - cookie}); + cookie, this_config.toString()}); } continue; } @@ -851,8 +898,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( if (!offset.has_value()) { if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY, - this_config.toString(), - cookie}); + cookie, this_config.toString()}); } continue; } @@ -865,8 +911,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back(Resolution::Step{resolution_type, - this_config.toString(), - cookie}); + cookie, this_config.toString()}); } // Any configuration will suffice, so break. @@ -895,11 +940,11 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( .entry = *entry, .config = *best_config, .type_flags = type_flags, + .dynamic_ref_table = package_group.dynamic_ref_table.get(), .package_name = &best_package->GetPackageName(), .type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1), .entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(), (*best_entry_verified)->key()), - .dynamic_ref_table = package_group.dynamic_ref_table.get(), }; } @@ -943,27 +988,40 @@ std::string AssetManager2::GetLastResourceResolution() const { } std::stringstream log_stream; - log_stream << base::StringPrintf("Resolution for 0x%08x %s\n" - "\tFor config - %s", resid, resource_name_string.c_str(), - configuration_.toString().c_str()); - + if (configurations_.size() == 1) { + log_stream << base::StringPrintf("Resolution for 0x%08x %s\n" + "\tFor config - %s", resid, resource_name_string.c_str(), + configurations_[0].toString().c_str()); + } else { + ResTable_config conf = configurations_[0]; + conf.clearLocale(); + log_stream << base::StringPrintf("Resolution for 0x%08x %s\n\tFor config - %s and locales", + resid, resource_name_string.c_str(), conf.toString().c_str()); + char str[40]; + str[0] = '\0'; + for(auto iter = configurations_.begin(); iter < configurations_.end(); iter++) { + iter->getBcp47Locale(str); + log_stream << base::StringPrintf(" %s%s", str, iter < configurations_.end() ? "," : ""); + } + } for (const Resolution::Step& step : last_resolution_.steps) { - const static std::unordered_map<Resolution::Step::Type, const char*> kStepStrings = { - {Resolution::Step::Type::INITIAL, "Found initial"}, - {Resolution::Step::Type::BETTER_MATCH, "Found better"}, - {Resolution::Step::Type::OVERLAID, "Overlaid"}, - {Resolution::Step::Type::OVERLAID_INLINE, "Overlaid inline"}, - {Resolution::Step::Type::SKIPPED, "Skipped"}, - {Resolution::Step::Type::NO_ENTRY, "No entry"} + constexpr static std::array kStepStrings = { + "Found initial", + "Found better", + "Overlaid", + "Overlaid inline", + "Skipped", + "No entry" }; - const auto prefix = kStepStrings.find(step.type); - if (prefix == kStepStrings.end()) { + if (step.type < Resolution::Step::Type::INITIAL + || step.type > Resolution::Step::Type::NO_ENTRY) { continue; } + const auto prefix = kStepStrings[int(step.type) - int(Resolution::Step::Type::INITIAL)]; const auto& assets = GetApkAssets(step.cookie); - log_stream << "\n\t" << prefix->second << ": " - << (assets ? assets->GetDebugName() : "<null>") << " #" << step.cookie; + log_stream << "\n\t" << prefix << ": " << (assets ? assets->GetDebugName() : "<null>") + << " #" << step.cookie; if (!step.config_name.empty()) { log_stream << " - " << step.config_name; } @@ -1100,16 +1158,19 @@ base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference( } } -const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) const { - auto cached_iter = cached_bag_resid_stacks_.find(resid); - if (cached_iter != cached_bag_resid_stacks_.end()) { - return cached_iter->second; +base::expected<const std::vector<uint32_t>*, NullOrIOError> AssetManager2::GetBagResIdStack( + uint32_t resid) const { + auto it = cached_bag_resid_stacks_.find(resid); + if (it != cached_bag_resid_stacks_.end()) { + return &it->second; + } + std::vector<uint32_t> stacks; + if (auto maybe_bag = GetBag(resid, stacks); UNLIKELY(IsIOError(maybe_bag))) { + return base::unexpected(maybe_bag.error()); } - std::vector<uint32_t> found_resids; - GetBag(resid, found_resids); - cached_bag_resid_stacks_.emplace(resid, found_resids); - return found_resids; + it = cached_bag_resid_stacks_.emplace(resid, std::move(stacks)).first; + return &it->second; } base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::ResolveBag( @@ -1126,9 +1187,15 @@ base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::ResolveBag( } base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const { - std::vector<uint32_t> found_resids; - const auto bag = GetBag(resid, found_resids); - cached_bag_resid_stacks_.emplace(resid, std::move(found_resids)); + auto resid_stacks_it = cached_bag_resid_stacks_.find(resid); + if (resid_stacks_it == cached_bag_resid_stacks_.end()) { + resid_stacks_it = cached_bag_resid_stacks_.emplace(resid, std::vector<uint32_t>{}).first; + } + const auto bag = GetBag(resid, resid_stacks_it->second); + if (UNLIKELY(IsIOError(bag))) { + cached_bag_resid_stacks_.erase(resid_stacks_it); + return base::unexpected(bag.error()); + } return bag; } @@ -1426,11 +1493,14 @@ void AssetManager2::RebuildFilterList() { package.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) { FilteredConfigGroup* group = nullptr; for (const auto& type_entry : type_spec.type_entries) { - if (type_entry.config.match(configuration_)) { - if (!group) { - group = &package.filtered_configs_.editItemAt(type_id - 1); + for (auto & config : configurations_) { + if (type_entry.config.match(config)) { + if (!group) { + group = &package.filtered_configs_.editItemAt(type_id - 1); + } + group->type_entries.push_back(&type_entry); + break; } - group->type_entries.push_back(&type_entry); } } }); @@ -1441,25 +1511,40 @@ void AssetManager2::RebuildFilterList() { } void AssetManager2::InvalidateCaches(uint32_t diff) { - cached_bag_resid_stacks_.clear(); + cached_resolved_values_.clear(); if (diff == 0xffffffffu) { // Everything must go. cached_bags_.clear(); + cached_bag_resid_stacks_.clear(); return; } // Be more conservative with what gets purged. Only if the bag has other possible // variations with respect to what changed (diff) should we remove it. - for (auto iter = cached_bags_.cbegin(); iter != cached_bags_.cend();) { - if (diff & iter->second->type_spec_flags) { - iter = cached_bags_.erase(iter); + for (auto stack_it = cached_bag_resid_stacks_.begin(); + stack_it != cached_bag_resid_stacks_.end();) { + const auto it = cached_bags_.find(stack_it->first); + if (it == cached_bags_.end()) { + stack_it = cached_bag_resid_stacks_.erase(stack_it); + } else if ((diff & it->second->type_spec_flags) != 0) { + cached_bags_.erase(it); + stack_it = cached_bag_resid_stacks_.erase(stack_it); } else { - ++iter; + ++stack_it; // Keep the item in both caches. } } - cached_resolved_values_.clear(); + // Need to ensure that both bag caches are consistent, as we populate them in the same function. + // Iterate over the cached bags to erase the items without the corresponding resid_stack cache + // items. + for (auto it = cached_bags_.begin(); it != cached_bags_.end();) { + if ((diff & it->second->type_spec_flags) != 0) { + it = cached_bags_.erase(it); + } else { + ++it; + } + } } uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const { diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 89835742c8ff..5f98b8f8db43 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -294,14 +294,14 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie dtohl(header->version), kIdmapCurrentVersion); return {}; } + std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path"); + if (!target_path) { + return {}; + } std::optional<std::string_view> overlay_path = ReadString(&data_ptr, &data_size, "overlay path"); if (!overlay_path) { return {}; } - std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path"); - if (!target_path) { - return {}; - } if (!ReadString(&data_ptr, &data_size, "target name") || !ReadString(&data_ptr, &data_size, "debug info")) { return {}; @@ -364,7 +364,7 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie return std::unique_ptr<LoadedIdmap>( new LoadedIdmap(std::string(idmap_path), header, data_header, target_entries, target_inline_entries, target_inline_entry_values, configurations, - overlay_entries, std::move(idmap_string_pool), *target_path, *overlay_path)); + overlay_entries, std::move(idmap_string_pool), *overlay_path, *target_path)); } bool LoadedIdmap::IsUpToDate() const { diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index fbfae5e2bcbe..c9d5e074271b 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -494,6 +494,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &loaded_package->package_name_); + const bool only_overlayable = (property_flags & PROPERTY_ONLY_OVERLAYABLES) != 0; + // A map of TypeSpec builders, each associated with an type index. // We use these to accumulate the set of Types available for a TypeSpec, and later build a single, // contiguous block of memory that holds all the Types together with the TypeSpec. @@ -502,6 +504,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, ChunkIterator iter(chunk.data_ptr(), chunk.data_size()); while (iter.HasNext()) { const Chunk child_chunk = iter.Next(); + if (only_overlayable && child_chunk.type() != RES_TABLE_OVERLAYABLE_TYPE) { + continue; + } switch (child_chunk.type()) { case RES_STRING_POOL_TYPE: { const auto pool_address = child_chunk.header<ResChunk_header>(); @@ -655,6 +660,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, << name_to_actor_it->first << "'."; return {}; } + if (only_overlayable) { + break; + } // Iterate over the overlayable policy chunks contained within the overlayable chunk data ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size()); @@ -800,14 +808,21 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap); } + const bool only_overlayable = (property_flags & PROPERTY_ONLY_OVERLAYABLES) != 0; + const size_t package_count = dtohl(header->packageCount); size_t packages_seen = 0; - packages_.reserve(package_count); + if (!only_overlayable) { + packages_.reserve(package_count); + } ChunkIterator iter(chunk.data_ptr(), chunk.data_size()); while (iter.HasNext()) { const Chunk child_chunk = iter.Next(); + if (only_overlayable && child_chunk.type() != RES_TABLE_PACKAGE_TYPE) { + continue; + } switch (child_chunk.type()) { case RES_STRING_POOL_TYPE: // Only use the first string pool. Ignore others. @@ -837,6 +852,10 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, return false; } packages_.push_back(std::move(loaded_package)); + if (only_overlayable) { + // Overlayable is always in the first package, no need to process anything else. + return true; + } } break; default: diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index c13827fe29c3..4c992becda7c 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -1769,13 +1769,21 @@ ResXMLTree::~ResXMLTree() status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) { + const ResChunk_header* chunk = nullptr; + const ResChunk_header* lastChunk = nullptr; + uninit(); mEventCode = START_DOCUMENT; if (!data || !size) { return (mError=BAD_TYPE); } - + if (size < sizeof(ResXMLTree_header)) { + ALOGW("Bad XML block: total size %d is less than the header size %d\n", + int(size), int(sizeof(ResXMLTree_header))); + mError = BAD_TYPE; + goto done; + } if (copyData) { mOwnedData = malloc(size); if (mOwnedData == NULL) { @@ -1792,9 +1800,15 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) (int)dtohs(mHeader->header.headerSize), (int)dtohl(mHeader->header.size), (int)size); mError = BAD_TYPE; - restart(); - return mError; + goto done; } + if (dtohs(mHeader->header.type) != RES_XML_TYPE) { + ALOGW("Bad XML block: expected root block type %d, got %d\n", + int(RES_XML_TYPE), int(dtohs(mHeader->header.type))); + mError = BAD_TYPE; + goto done; + } + mDataEnd = ((const uint8_t*)mHeader) + mSize; mStrings.uninit(); @@ -1804,9 +1818,8 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) // First look for a couple interesting chunks: the string block // and first XML node. - const ResChunk_header* chunk = - (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize)); - const ResChunk_header* lastChunk = chunk; + chunk = (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize)); + lastChunk = chunk; while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) && ((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) { status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML"); @@ -1860,7 +1873,11 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) mError = mStrings.getError(); done: - restart(); + if (mError) { + uninit(); + } else { + restart(); + } return mError; } @@ -2551,6 +2568,22 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o, return false; } +bool ResTable_config::isBetterThanBeforeLocale(const ResTable_config& o, + const ResTable_config* requested) const { + if (requested) { + if (imsi || o.imsi) { + if ((mcc != o.mcc) && requested->mcc) { + return (mcc); + } + + if ((mnc != o.mnc) && requested->mnc) { + return (mnc); + } + } + } + return false; +} + bool ResTable_config::isBetterThan(const ResTable_config& o, const ResTable_config* requested) const { if (requested) { @@ -5436,37 +5469,66 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) return U16StringToInt(s, len, outValue); } -bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) -{ - while (len > 0 && isspace16(*s)) { - s++; - len--; +template <typename T> +bool parseFloatingPoint(const char16_t* inBuf, size_t inLen, char* tempBuf, + const char** outEnd, T& out){ + while (inLen > 0 && isspace16(*inBuf)) { + inBuf++; + inLen--; } - if (len <= 0) { + if (inLen <= 0) { return false; } - char buf[128]; int i=0; - while (len > 0 && *s != 0 && i < 126) { - if (*s > 255) { + while (inLen > 0 && *inBuf != 0 && i < 126) { + if (*inBuf > 255) { return false; } - buf[i++] = *s++; - len--; + tempBuf[i++] = *inBuf++; + inLen--; } - if (len > 0) { + if (inLen > 0) { + return false; + } + if ((tempBuf[0] < '0' || tempBuf[0] > '9') && tempBuf[0] != '.' && tempBuf[0] != '-' && tempBuf[0] != '+') { return false; } - if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') { + + tempBuf[i] = 0; + if constexpr(std::is_same_v<T, float>) { + out = strtof(tempBuf, (char**)outEnd); + } else { + out = strtod(tempBuf, (char**)outEnd); + } + return true; +} + +bool ResTable::stringToDouble(const char16_t* s, size_t len, double& d){ + char buf[128]; + const char* end = nullptr; + if (!parseFloatingPoint(s, len, buf, &end, d)) { return false; } - buf[i] = 0; - const char* end; - float f = strtof(buf, (char**)&end); + while (*end != 0 && isspace((unsigned char)*end)) { + end++; + } + + return *end == 0; +} + +bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) +{ + char buf[128]; + const char* end = nullptr; + float f; + + if (!parseFloatingPoint(s, len, buf, &end, f)) { + return false; + } if (*end != 0 && !isspace((unsigned char)*end)) { // Might be a unit... diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index 52e7a70521a1..d7b5914130ee 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -40,17 +40,24 @@ class _ZipEntryRO { public: ZipEntry entry; std::string_view name; - void *cookie; + void *cookie = nullptr; - _ZipEntryRO() : cookie(NULL) {} + _ZipEntryRO() = default; ~_ZipEntryRO() { - EndIteration(cookie); + EndIteration(cookie); + } + + android::ZipEntryRO convertToPtr() { + _ZipEntryRO* result = new _ZipEntryRO; + result->entry = std::move(this->entry); + result->name = std::move(this->name); + result->cookie = std::exchange(this->cookie, nullptr); + return result; } private: - _ZipEntryRO(const _ZipEntryRO& other); - _ZipEntryRO& operator=(const _ZipEntryRO& other); + DISALLOW_COPY_AND_ASSIGN(_ZipEntryRO); }; ZipFileRO::~ZipFileRO() { @@ -94,17 +101,15 @@ ZipFileRO::~ZipFileRO() { ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const { - _ZipEntryRO* data = new _ZipEntryRO; - - data->name = entryName; + _ZipEntryRO data; + data.name = entryName; - const int32_t error = FindEntry(mHandle, entryName, &(data->entry)); + const int32_t error = FindEntry(mHandle, entryName, &(data.entry)); if (error) { - delete data; - return NULL; + return nullptr; } - return (ZipEntryRO) data; + return data.convertToPtr(); } /* @@ -143,35 +148,50 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, } bool ZipFileRO::startIteration(void** cookie) { - return startIteration(cookie, NULL, NULL); + return startIteration(cookie, nullptr, nullptr); } -bool ZipFileRO::startIteration(void** cookie, const char* prefix, const char* suffix) -{ - _ZipEntryRO* ze = new _ZipEntryRO; - int32_t error = StartIteration(mHandle, &(ze->cookie), +bool ZipFileRO::startIteration(void** cookie, const char* prefix, const char* suffix) { + auto result = startIterationOrError(prefix, suffix); + if (!result.ok()) { + return false; + } + *cookie = result.value(); + return true; +} + +base::expected<void*, int32_t> +ZipFileRO::startIterationOrError(const char* prefix, const char* suffix) { + _ZipEntryRO ze; + int32_t error = StartIteration(mHandle, &(ze.cookie), prefix ? prefix : "", suffix ? suffix : ""); if (error) { ALOGW("Could not start iteration over %s: %s", mFileName != NULL ? mFileName : "<null>", ErrorCodeString(error)); - delete ze; - return false; + return base::unexpected(error); } - *cookie = ze; - return true; + return ze.convertToPtr(); } -ZipEntryRO ZipFileRO::nextEntry(void* cookie) -{ +ZipEntryRO ZipFileRO::nextEntry(void* cookie) { + auto result = nextEntryOrError(cookie); + if (!result.ok()) { + return nullptr; + } + return result.value(); +} + +base::expected<ZipEntryRO, int32_t> ZipFileRO::nextEntryOrError(void* cookie) { _ZipEntryRO* ze = reinterpret_cast<_ZipEntryRO*>(cookie); int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name)); if (error) { if (error != -1) { ALOGW("Error iteration over %s: %s", mFileName != NULL ? mFileName : "<null>", ErrorCodeString(error)); + return base::unexpected(error); } - return NULL; + return nullptr; } return &(ze->entry); diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index fc391bc2ce67..d9ff35b49e0a 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -100,7 +100,7 @@ class AssetManager2 { using ApkAssetsWPtr = wp<const ApkAssets>; using ApkAssetsList = std::span<const ApkAssetsPtr>; - AssetManager2() = default; + AssetManager2(); explicit AssetManager2(AssetManager2&& other) = default; AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration); @@ -156,10 +156,14 @@ class AssetManager2 { // Sets/resets the configuration for this AssetManager. This will cause all // caches that are related to the configuration change to be invalidated. - void SetConfiguration(const ResTable_config& configuration); + void SetConfigurations(std::vector<ResTable_config> configurations); - inline const ResTable_config& GetConfiguration() const { - return configuration_; + inline const std::vector<ResTable_config>& GetConfigurations() const { + return configurations_; + } + + inline void SetDefaultLocale(uint32_t default_locale) { + default_locale_ = default_locale; } // Returns all configurations for which there are resources defined, or an I/O error if reading @@ -243,9 +247,14 @@ class AssetManager2 { friend AssetManager2; friend Theme; SelectedValue() = default; - SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry) : - cookie(entry.cookie), data(entry.value.data), type(entry.value.dataType), - flags(bag->type_spec_flags), resid(0U), config({}) {}; + SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry) + : cookie(entry.cookie), + data(entry.value.data), + type(entry.value.dataType), + flags(bag->type_spec_flags), + resid(0U), + config() { + } // The cookie representing the ApkAssets in which the value resides. ApkAssetsCookie cookie = kInvalidCookie; @@ -327,7 +336,8 @@ class AssetManager2 { // resource data failed. base::expected<uint32_t, NullOrIOError> GetResourceTypeSpecFlags(uint32_t resid) const; - const std::vector<uint32_t> GetBagResIdStack(uint32_t resid) const; + base::expected<const std::vector<uint32_t>*, NullOrIOError> GetBagResIdStack( + uint32_t resid) const; // Resets the resource resolution structures in preparation for the next resource retrieval. void ResetResourceResolution() const; @@ -459,9 +469,11 @@ class AssetManager2 { // without taking too much memory. std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_; - // The current configuration set for this AssetManager. When this changes, cached resources + uint32_t default_locale_; + + // The current configurations set for this AssetManager. When this changes, cached resources // may need to be purged. - ResTable_config configuration_ = {}; + std::vector<ResTable_config> configurations_; // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. @@ -495,10 +507,10 @@ class AssetManager2 { // Marks what kind of override this step was. Type type; + ApkAssetsCookie cookie = kInvalidCookie; + // Built name of configuration for this step. String8 config_name; - - ApkAssetsCookie cookie = kInvalidCookie; }; // Last resolved resource ID. diff --git a/libs/androidfw/include/androidfw/Errors.h b/libs/androidfw/include/androidfw/Errors.h index 948162d10480..6667747fc581 100644 --- a/libs/androidfw/include/androidfw/Errors.h +++ b/libs/androidfw/include/androidfw/Errors.h @@ -34,7 +34,7 @@ using NullOrIOError = std::variant<std::nullopt_t, IOError>; // Checks whether the result holds an unexpected I/O error. template <typename T> -static inline bool IsIOError(const base::expected<T, NullOrIOError> result) { +static inline bool IsIOError(const base::expected<T, NullOrIOError>& result) { return !result.has_value() && std::holds_alternative<IOError>(result.error()); } diff --git a/libs/androidfw/include/androidfw/IDiagnostics.h b/libs/androidfw/include/androidfw/IDiagnostics.h index 4d5844eaa069..865a298f8389 100644 --- a/libs/androidfw/include/androidfw/IDiagnostics.h +++ b/libs/androidfw/include/androidfw/IDiagnostics.h @@ -86,6 +86,17 @@ struct IDiagnostics { DiagMessageActual actual = message.Build(); Log(Level::Note, actual); } + + virtual void SetVerbose(bool val) { + verbose_ = val; + } + + virtual bool IsVerbose() { + return verbose_; + } + + private: + bool verbose_ = false; }; class SourcePathDiagnostics : public IDiagnostics { @@ -105,6 +116,14 @@ class SourcePathDiagnostics : public IDiagnostics { return error; } + void SetVerbose(bool val) override { + diag_->SetVerbose(val); + } + + bool IsVerbose() override { + return diag_->IsVerbose(); + } + private: Source source_; IDiagnostics* diag_; diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 4d12885ad291..3a7287187781 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -96,6 +96,9 @@ enum : package_property_t { // The apk assets is owned by the application running in this process and incremental crash // protections for this APK must be disabled. PROPERTY_DISABLE_INCREMENTAL_HARDENING = 1U << 4U, + + // The apk assets only contain the overlayable declarations information. + PROPERTY_ONLY_OVERLAYABLES = 1U << 5U, }; struct OverlayableInfo { diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 631bda4f886c..fdb355192676 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1375,6 +1375,8 @@ struct ResTable_config // match the requested configuration at all. bool isLocaleBetterThan(const ResTable_config& o, const ResTable_config* requested) const; + bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const; + String8 toString() const; }; @@ -1870,6 +1872,8 @@ struct FabricatedOverlayEntryParameters { DataValue data_value; std::string data_string_value; std::optional<android::base::borrowed_fd> data_binary_value; + off64_t binary_data_offset; + size_t binary_data_size; std::string configuration; }; @@ -2162,6 +2166,7 @@ public: static bool stringToInt(const char16_t* s, size_t len, Res_value* outValue); static bool stringToFloat(const char16_t* s, size_t len, Res_value* outValue); + static bool stringToDouble(const char16_t* s, size_t len, double& outValue); // Used with stringToValue. class Accessor diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h index 10f6d0655bf4..be1f98f4843d 100644 --- a/libs/androidfw/include/androidfw/ZipFileRO.h +++ b/libs/androidfw/include/androidfw/ZipFileRO.h @@ -37,6 +37,8 @@ #include <unistd.h> #include <time.h> +#include <android-base/expected.h> + #include <util/map_ptr.h> #include <utils/Compat.h> @@ -102,6 +104,11 @@ public: */ bool startIteration(void** cookie); bool startIteration(void** cookie, const char* prefix, const char* suffix); + /* + * Same as above, but returns the error code in case of failure. + * #see libziparchive/zip_error.h. + */ + base::expected<void*, int32_t> startIterationOrError(const char* prefix, const char* suffix); /** * Return the next entry in iteration order, or NULL if there are no more @@ -109,6 +116,12 @@ public: */ ZipEntryRO nextEntry(void* cookie); + /** + * Same as above, but returns the error code in case of failure. + * #see libziparchive/zip_error.h. + */ + base::expected<ZipEntryRO, int32_t> nextEntryOrError(void* cookie); + void endIteration(void* cookie); void releaseEntry(ZipEntryRO entry) const; diff --git a/libs/androidfw/tests/ApkParsing_test.cpp b/libs/androidfw/tests/ApkParsing_test.cpp index 62e88c619e5c..ac1dc9b88463 100644 --- a/libs/androidfw/tests/ApkParsing_test.cpp +++ b/libs/androidfw/tests/ApkParsing_test.cpp @@ -74,4 +74,10 @@ TEST(ApkParsingTest, InvalidFileAtRoot) { auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false); ASSERT_THAT(lastSlash, IsNull()); } + +TEST(ApkParsingTest, InvalidPrefix) { + const char* path = "assets/libhello.so"; + auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false); + ASSERT_THAT(lastSlash, IsNull()); +} }
\ No newline at end of file diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 6fae72a6d10e..2caa98c35971 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -228,10 +228,12 @@ static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) { ResTable_config config; memset(&config, 0, sizeof(config)); + std::vector<ResTable_config> configs; + configs.push_back(config); while (state.KeepRunning()) { - config.sdkVersion = ~config.sdkVersion; - assets.SetConfiguration(config); + configs[0].sdkVersion = ~configs[0].sdkVersion; + assets.SetConfigurations(configs); } } BENCHMARK(BM_AssetManagerSetConfigurationFramework); diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index df3fa02ce44c..c62f095e9dac 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -113,7 +113,7 @@ TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) { desired_config.language[1] = 'e'; AssetManager2 assetmanager; - assetmanager.SetConfiguration(desired_config); + assetmanager.SetConfigurations({desired_config}); assetmanager.SetApkAssets({basic_assets_}); auto value = assetmanager.GetResource(basic::R::string::test1); @@ -137,7 +137,7 @@ TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) { desired_config.language[1] = 'e'; AssetManager2 assetmanager; - assetmanager.SetConfiguration(desired_config); + assetmanager.SetConfigurations({desired_config}); assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_}); auto value = assetmanager.GetResource(basic::R::string::test1); @@ -466,10 +466,10 @@ TEST_F(AssetManager2Test, ResolveDeepIdReference) { TEST_F(AssetManager2Test, DensityOverride) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_, basic_xhdpi_assets_, basic_xxhdpi_assets_}); - assetmanager.SetConfiguration({ + assetmanager.SetConfigurations({{ .density = ResTable_config::DENSITY_XHIGH, .sdkVersion = 21, - }); + }}); auto value = assetmanager.GetResource(basic::R::string::density, false /*may_be_bag*/); ASSERT_TRUE(value.has_value()); @@ -721,7 +721,7 @@ TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) { ResTable_config desired_config; AssetManager2 assetmanager; - assetmanager.SetConfiguration(desired_config); + assetmanager.SetConfigurations({desired_config}); assetmanager.SetApkAssets({basic_assets_}); assetmanager.SetResourceResolutionLoggingEnabled(false); @@ -736,7 +736,7 @@ TEST_F(AssetManager2Test, GetLastPathWithoutResolutionReturnsEmpty) { ResTable_config desired_config; AssetManager2 assetmanager; - assetmanager.SetConfiguration(desired_config); + assetmanager.SetConfigurations({desired_config}); assetmanager.SetApkAssets({basic_assets_}); auto result = assetmanager.GetLastResourceResolution(); @@ -751,7 +751,7 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) { AssetManager2 assetmanager; assetmanager.SetResourceResolutionLoggingEnabled(true); - assetmanager.SetConfiguration(desired_config); + assetmanager.SetConfigurations({desired_config}); assetmanager.SetApkAssets({basic_assets_}); auto value = assetmanager.GetResource(basic::R::string::test1); @@ -774,7 +774,7 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { AssetManager2 assetmanager; assetmanager.SetResourceResolutionLoggingEnabled(true); - assetmanager.SetConfiguration(desired_config); + assetmanager.SetConfigurations({desired_config}); assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_}); auto value = assetmanager.GetResource(basic::R::string::test1); @@ -796,7 +796,7 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { AssetManager2 assetmanager; assetmanager.SetResourceResolutionLoggingEnabled(true); - assetmanager.SetConfiguration(desired_config); + assetmanager.SetConfigurations({desired_config}); assetmanager.SetApkAssets({basic_assets_}); auto value = assetmanager.GetResource(basic::R::string::test1); @@ -817,7 +817,7 @@ TEST_F(AssetManager2Test, GetOverlayablesToString) { AssetManager2 assetmanager; assetmanager.SetResourceResolutionLoggingEnabled(true); - assetmanager.SetConfiguration(desired_config); + assetmanager.SetConfigurations({desired_config}); assetmanager.SetApkAssets({overlayable_assets_}); const auto map = assetmanager.GetOverlayableMapForPackage(0x7f); diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index b97dd96f8934..8b883f4ed1df 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -66,7 +66,7 @@ void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_ AssetManager2 assetmanager; assetmanager.SetApkAssets(apk_assets); if (config != nullptr) { - assetmanager.SetConfiguration(*config); + assetmanager.SetConfigurations({*config}); } while (state.KeepRunning()) { diff --git a/libs/androidfw/tests/Generic_bench.cpp b/libs/androidfw/tests/Generic_bench.cpp new file mode 100644 index 000000000000..4c978e889f83 --- /dev/null +++ b/libs/androidfw/tests/Generic_bench.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> + +#include <map> +#include <unordered_map> + +#include "benchmark/benchmark.h" + +namespace android { + +template <class Map = std::unordered_map<uint32_t, std::vector<uint32_t>>> +static Map prepare_map() { + Map map; + std::vector<uint32_t> vec; + for (int i = 0; i < 1000; ++i) { + map.emplace(i, vec); + } + return map; +} + +static void BM_hashmap_emplace_same(benchmark::State& state) { + auto map = prepare_map<>(); + auto val = map.size() - 1; + std::vector<uint32_t> vec; + for (auto&& _ : state) { + benchmark::DoNotOptimize(map.emplace(val, vec)); + } +} +BENCHMARK(BM_hashmap_emplace_same); +static void BM_hashmap_try_emplace_same(benchmark::State& state) { + auto map = prepare_map(); + auto val = map.size() - 1; + for (auto&& _ : state) { + benchmark::DoNotOptimize(map.try_emplace(val)); + } +} +BENCHMARK(BM_hashmap_try_emplace_same); +static void BM_hashmap_find(benchmark::State& state) { + auto map = prepare_map<>(); + auto val = map.size() - 1; + for (auto&& _ : state) { + benchmark::DoNotOptimize(map.find(val)); + } +} +BENCHMARK(BM_hashmap_find); + +static void BM_hashmap_emplace_diff(benchmark::State& state) { + auto map = prepare_map<>(); + std::vector<uint32_t> vec; + auto i = map.size(); + for (auto&& _ : state) { + map.emplace(i++, vec); + } +} +BENCHMARK(BM_hashmap_emplace_diff); +static void BM_hashmap_try_emplace_diff(benchmark::State& state) { + auto map = prepare_map(); + auto i = map.size(); + for (auto&& _ : state) { + map.try_emplace(i++); + } +} +BENCHMARK(BM_hashmap_try_emplace_diff); +static void BM_hashmap_find_emplace_diff(benchmark::State& state) { + auto map = prepare_map<>(); + std::vector<uint32_t> vec; + auto i = map.size(); + for (auto&& _ : state) { + if (map.find(i++) == map.end()) { + map.emplace(i - 1, vec); + } + } +} +BENCHMARK(BM_hashmap_find_emplace_diff); + +static void BM_treemap_emplace_same(benchmark::State& state) { + auto map = prepare_map<std::map<uint32_t, std::vector<uint32_t>>>(); + auto val = map.size() - 1; + std::vector<uint32_t> vec; + for (auto&& _ : state) { + benchmark::DoNotOptimize(map.emplace(val, vec)); + } +} +BENCHMARK(BM_treemap_emplace_same); +static void BM_treemap_try_emplace_same(benchmark::State& state) { + auto map = prepare_map<std::map<uint32_t, std::vector<uint32_t>>>(); + auto val = map.size() - 1; + for (auto&& _ : state) { + benchmark::DoNotOptimize(map.try_emplace(val)); + } +} +BENCHMARK(BM_treemap_try_emplace_same); +static void BM_treemap_find(benchmark::State& state) { + auto map = prepare_map<std::map<uint32_t, std::vector<uint32_t>>>(); + auto val = map.size() - 1; + for (auto&& _ : state) { + benchmark::DoNotOptimize(map.find(val)); + } +} +BENCHMARK(BM_treemap_find); + +static void BM_treemap_emplace_diff(benchmark::State& state) { + auto map = prepare_map<std::map<uint32_t, std::vector<uint32_t>>>(); + std::vector<uint32_t> vec; + auto i = map.size(); + for (auto&& _ : state) { + map.emplace(i++, vec); + } +} +BENCHMARK(BM_treemap_emplace_diff); +static void BM_treemap_try_emplace_diff(benchmark::State& state) { + auto map = prepare_map(); + auto i = map.size(); + for (auto&& _ : state) { + map.try_emplace(i++); + } +} +BENCHMARK(BM_treemap_try_emplace_diff); +static void BM_treemap_find_emplace_diff(benchmark::State& state) { + auto map = prepare_map(); + std::vector<uint32_t> vec; + auto i = map.size(); + for (auto&& _ : state) { + if (map.find(i++) == map.end()) { + map.emplace(i - 1, vec); + } + } +} +BENCHMARK(BM_treemap_find_emplace_diff); + +} // namespace android
\ No newline at end of file diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index e08a6a7f277d..181d1411fb91 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -260,7 +260,7 @@ TEST_F(ThemeTest, ThemeRebase) { ResTable_config night{}; night.uiMode = ResTable_config::UI_MODE_NIGHT_YES; night.version = 8u; - am_night.SetConfiguration(night); + am_night.SetConfigurations({night}); auto theme = am.NewTheme(); { diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index c87c15669a09..ff1eedb8eacb 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -28,6 +28,19 @@ license { ], } +aconfig_declarations { + name: "hwui_flags", + package: "com.android.graphics.hwui.flags", + srcs: [ + "aconfig/hwui_flags.aconfig", + ], +} + +cc_aconfig_library { + name: "hwui_flags_cc_lib", + aconfig_declarations: "hwui_flags", +} + cc_defaults { name: "hwui_defaults", defaults: [ @@ -44,7 +57,7 @@ cc_defaults { "-DEGL_EGLEXT_PROTOTYPES", "-DGL_GLEXT_PROTOTYPES", "-DATRACE_TAG=ATRACE_TAG_VIEW", - "-DLOG_TAG=\"OpenGLRenderer\"", + "-DLOG_TAG=\"HWUI\"", "-Wall", "-Wthread-safety", "-Wno-unused-parameter", @@ -130,6 +143,7 @@ cc_defaults { "libcrypto", "libsync", "libui", + "aconfig_text_flags_c_lib", ], static_libs: [ "libEGL_blobCache", @@ -139,6 +153,7 @@ cc_defaults { "libstatspull_lazy", "libstatssocket_lazy", "libtonemap", + "hwui_flags_cc_lib", ], }, host: { @@ -514,6 +529,7 @@ cc_defaults { "canvas/CanvasOpRasterizer.cpp", "effects/StretchEffect.cpp", "effects/GainmapRenderer.cpp", + "pipeline/skia/BackdropFilterDrawable.cpp", "pipeline/skia/HolePunch.cpp", "pipeline/skia/SkiaDisplayList.cpp", "pipeline/skia/SkiaRecordingCanvas.cpp", @@ -697,11 +713,13 @@ cc_test { ], static_libs: [ + "libflagtest", "libgmock", "libhwui_static", ], shared_libs: [ "libmemunreachable", + "server_configurable_flags", ], srcs: [ "tests/unit/main.cpp", @@ -741,6 +759,7 @@ cc_test { "tests/unit/TestUtilsTests.cpp", "tests/unit/ThreadBaseTests.cpp", "tests/unit/TypefaceTests.cpp", + "tests/unit/UnderlineTest.cpp", "tests/unit/VectorDrawableTests.cpp", "tests/unit/WebViewFunctorManagerTests.cpp", ], diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp index b656b6ac8204..4d020c567972 100644 --- a/libs/hwui/AutoBackendTextureRelease.cpp +++ b/libs/hwui/AutoBackendTextureRelease.cpp @@ -16,6 +16,11 @@ #include "AutoBackendTextureRelease.h" +#include <SkImage.h> +#include <include/gpu/ganesh/SkImageGanesh.h> +#include <include/gpu/GrDirectContext.h> +#include <include/gpu/GrBackendSurface.h> +#include <include/gpu/MutableTextureState.h> #include "renderthread/RenderThread.h" #include "utils/Color.h" #include "utils/PaintUtils.h" @@ -30,15 +35,47 @@ AutoBackendTextureRelease::AutoBackendTextureRelease(GrDirectContext* context, AHardwareBuffer_Desc desc; AHardwareBuffer_describe(buffer, &desc); bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); - GrBackendFormat backendFormat = - GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false); + + GrBackendFormat backendFormat; + GrBackendApi backend = context->backend(); + if (backend == GrBackendApi::kOpenGL) { + backendFormat = + GrAHardwareBufferUtils::GetGLBackendFormat(context, desc.format, false); + mBackendTexture = + GrAHardwareBufferUtils::MakeGLBackendTexture(context, + buffer, + desc.width, + desc.height, + &mDeleteProc, + &mUpdateProc, + &mImageCtx, + createProtectedImage, + backendFormat, + false); + } else if (backend == GrBackendApi::kVulkan) { + backendFormat = + GrAHardwareBufferUtils::GetVulkanBackendFormat(context, + buffer, + desc.format, + false); + mBackendTexture = + GrAHardwareBufferUtils::MakeVulkanBackendTexture(context, + buffer, + desc.width, + desc.height, + &mDeleteProc, + &mUpdateProc, + &mImageCtx, + createProtectedImage, + backendFormat, + false); + } else { + LOG_ALWAYS_FATAL("Unexpected backend %d", backend); + } LOG_ALWAYS_FATAL_IF(!backendFormat.isValid(), __FILE__ " Invalid GrBackendFormat. GrBackendApi==%" PRIu32 ", AHardwareBuffer_Format==%" PRIu32 ".", static_cast<int>(context->backend()), desc.format); - mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture( - context, buffer, desc.width, desc.height, &mDeleteProc, &mUpdateProc, &mImageCtx, - createProtectedImage, backendFormat, false); LOG_ALWAYS_FATAL_IF(!mBackendTexture.isValid(), __FILE__ " Invalid GrBackendTexture. Width==%" PRIu32 ", height==%" PRIu32 ", protected==%d", @@ -70,7 +107,7 @@ void AutoBackendTextureRelease::unref(bool releaseImage) { // releaseProc is invoked by SkImage, when texture is no longer in use. // "releaseContext" contains an "AutoBackendTextureRelease*". -static void releaseProc(SkImage::ReleaseContext releaseContext) { +static void releaseProc(SkImages::ReleaseContext releaseContext) { AutoBackendTextureRelease* textureRelease = reinterpret_cast<AutoBackendTextureRelease*>(releaseContext); textureRelease->unref(false); @@ -83,10 +120,10 @@ void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer, AHardwareBuffer_describe(buffer, &desc); SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); // The following ref will be counteracted by Skia calling releaseProc, either during - // MakeFromTexture if there is a failure, or later when SkImage is discarded. It must - // be called before MakeFromTexture, otherwise Skia may remove HWUI's ref on failure. + // BorrowTextureFrom if there is a failure, or later when SkImage is discarded. It must + // be called before BorrowTextureFrom, otherwise Skia may remove HWUI's ref on failure. ref(); - mImage = SkImage::MakeFromTexture( + mImage = SkImages::BorrowTextureFrom( context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType, kPremul_SkAlphaType, uirenderer::DataSpaceToColorSpace(dataspace), releaseProc, this); } @@ -105,8 +142,8 @@ void AutoBackendTextureRelease::releaseQueueOwnership(GrDirectContext* context) LOG_ALWAYS_FATAL_IF(Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan); if (mBackendTexture.isValid()) { // Passing in VK_IMAGE_LAYOUT_UNDEFINED means we keep the old layout. - GrBackendSurfaceMutableState newState(VK_IMAGE_LAYOUT_UNDEFINED, - VK_QUEUE_FAMILY_FOREIGN_EXT); + skgpu::MutableTextureState newState(VK_IMAGE_LAYOUT_UNDEFINED, + VK_QUEUE_FAMILY_FOREIGN_EXT); // The unref for this ref happens in the releaseProc passed into setBackendTextureState. The // releaseProc callback will be made when the work to set the new state has finished on the diff --git a/libs/hwui/ColorFilter.h b/libs/hwui/ColorFilter.h new file mode 100644 index 000000000000..1a5b938d6eed --- /dev/null +++ b/libs/hwui/ColorFilter.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COLORFILTER_H_ +#define COLORFILTER_H_ + +#include <stdint.h> + +#include <memory> + +#include "GraphicsJNI.h" +#include "SkColorFilter.h" +#include "SkiaWrapper.h" + +namespace android { +namespace uirenderer { + +class ColorFilter : public SkiaWrapper<SkColorFilter> { +public: + static ColorFilter* fromJava(jlong handle) { return reinterpret_cast<ColorFilter*>(handle); } + +protected: + ColorFilter() = default; +}; + +class BlendModeColorFilter : public ColorFilter { +public: + BlendModeColorFilter(SkColor color, SkBlendMode mode) : mColor(color), mMode(mode) {} + +private: + sk_sp<SkColorFilter> createInstance() override { return SkColorFilters::Blend(mColor, mMode); } + +private: + const SkColor mColor; + const SkBlendMode mMode; +}; + +class LightingFilter : public ColorFilter { +public: + LightingFilter(SkColor mul, SkColor add) : mMul(mul), mAdd(add) {} + + void setMul(SkColor mul) { + mMul = mul; + discardInstance(); + } + + void setAdd(SkColor add) { + mAdd = add; + discardInstance(); + } + +private: + sk_sp<SkColorFilter> createInstance() override { return SkColorFilters::Lighting(mMul, mAdd); } + +private: + SkColor mMul; + SkColor mAdd; +}; + +class ColorMatrixColorFilter : public ColorFilter { +public: + ColorMatrixColorFilter(std::vector<float>&& matrix) : mMatrix(std::move(matrix)) {} + + void setMatrix(std::vector<float>&& matrix) { + mMatrix = std::move(matrix); + discardInstance(); + } + +private: + sk_sp<SkColorFilter> createInstance() override { + return SkColorFilters::Matrix(mMatrix.data()); + } + +private: + std::vector<float> mMatrix; +}; + +} // namespace uirenderer +} // namespace android + +#endif // COLORFILTER_H_ diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index eb5878d95561..8c180da9c84f 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -54,6 +54,8 @@ public: mImpl->updateChildren(std::move(updateFn)); } + void visit(std::function<void(const RenderNode&)> func) const { mImpl->visit(std::move(func)); } + [[nodiscard]] explicit operator bool() const { return mImpl.get() != nullptr; } diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index a18ba1c633b9..d21f07efe36a 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -14,7 +14,6 @@ * limitations under the License. */ -X(Flush) X(Save) X(Restore) X(SaveLayer) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt b/libs/hwui/FeatureFlags.h index ee55eca31072..ffb329d9f8e6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt +++ b/libs/hwui/FeatureFlags.h @@ -14,14 +14,27 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.bubble +#ifndef ANDROID_HWUI_FEATURE_FLAGS_H +#define ANDROID_HWUI_FEATURE_FLAGS_H -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +#ifdef __ANDROID__ +#include <com_android_text_flags.h> +#endif // __ANDROID__ -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -class DragToDismissBubbleScreenTestCfArm(flicker: LegacyFlickerTest) : - DragToDismissBubbleScreenTest(flicker) +namespace android { + +namespace text_feature { + +inline bool fix_double_underline() { +#ifdef __ANDROID__ + return com_android_text_flags_fix_double_underline(); +#else + return true; +#endif // __ANDROID__ +} + +} // namespace text_feature + +} // namespace android + +#endif // ANDROID_HWUI_FEATURE_FLAGS_H diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp index 8191f5e6a83a..a958a091a830 100644 --- a/libs/hwui/FrameInfo.cpp +++ b/libs/hwui/FrameInfo.cpp @@ -15,6 +15,8 @@ */ #include "FrameInfo.h" +#include <gui/TraceUtils.h> + #include <cstring> namespace android { @@ -51,6 +53,30 @@ static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 23, void FrameInfo::importUiThreadInfo(int64_t* info) { memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t)); + mSkippedFrameReason.reset(); +} + +const char* toString(SkippedFrameReason reason) { + switch (reason) { + case SkippedFrameReason::DrawingOff: + return "DrawingOff"; + case SkippedFrameReason::ContextIsStopped: + return "ContextIsStopped"; + case SkippedFrameReason::NothingToDraw: + return "NothingToDraw"; + case SkippedFrameReason::NoOutputTarget: + return "NoOutputTarget"; + case SkippedFrameReason::NoBuffer: + return "NoBuffer"; + case SkippedFrameReason::AlreadyDrawn: + return "AlreadyDrawn"; + } +} + +void FrameInfo::setSkippedFrameReason(android::uirenderer::SkippedFrameReason reason) { + ATRACE_FORMAT_INSTANT("Frame skipped: %s", toString(reason)); + addFlag(FrameInfoFlags::SkippedFrame); + mSkippedFrameReason = reason; } } /* namespace uirenderer */ diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index b15b6cb9a9ec..f7ad13978a30 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -16,15 +16,17 @@ #ifndef FRAMEINFO_H_ #define FRAMEINFO_H_ -#include "utils/Macros.h" - #include <cutils/compiler.h> +#include <memory.h> #include <utils/Timers.h> #include <array> -#include <memory.h> +#include <optional> #include <string> +#include "SkippedFrameInfo.h" +#include "utils/Macros.h" + namespace android { namespace uirenderer { @@ -186,8 +188,14 @@ public: return mFrameInfo[static_cast<int>(index)]; } + void setSkippedFrameReason(SkippedFrameReason reason); + inline std::optional<SkippedFrameReason> getSkippedFrameReason() const { + return mSkippedFrameReason; + } + private: int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::NumIndexes)]; + std::optional<SkippedFrameReason> mSkippedFrameReason; }; } /* namespace uirenderer */ diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp index 687e4dd324d3..59f21694fb77 100644 --- a/libs/hwui/FrameInfoVisualizer.cpp +++ b/libs/hwui/FrameInfoVisualizer.cpp @@ -148,7 +148,7 @@ void FrameInfoVisualizer::initializeRects(const int baseline, const int width) { int fast_i = 0, janky_i = 0; // Set the bottom of all the shapes to the baseline for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) { - if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) { + if (mFrameSource[fi].getSkippedFrameReason()) { continue; } float lineWidth = baseLineWidth; @@ -181,7 +181,7 @@ void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex en int janky_i = (mNumJankyRects - 1) * 4; for (size_t fi = 0; fi < mFrameSource.size(); fi++) { - if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) { + if (mFrameSource[fi].getSkippedFrameReason()) { continue; } diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index b7e99994355c..16de21def0e3 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -22,9 +22,11 @@ #include <GLES2/gl2ext.h> #include <GLES3/gl3.h> #include <GrDirectContext.h> +#include <GrTypes.h> #include <SkBitmap.h> #include <SkCanvas.h> #include <SkImage.h> +#include <SkImageAndroid.h> #include <SkImageInfo.h> #include <SkRefCnt.h> #include <gui/TraceUtils.h> @@ -262,8 +264,9 @@ private: } sk_sp<SkImage> image = - SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), ahb); - mGrContext->submit(true); + SkImages::TextureFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), + ahb); + mGrContext->submit(GrSyncCpu::kYes); uploadSucceeded = (image.get() != nullptr); }); diff --git a/libs/hwui/MemoryPolicy.cpp b/libs/hwui/MemoryPolicy.cpp index ca1312e75f4c..21f4ca79b68c 100644 --- a/libs/hwui/MemoryPolicy.cpp +++ b/libs/hwui/MemoryPolicy.cpp @@ -28,7 +28,10 @@ namespace android::uirenderer { constexpr static MemoryPolicy sDefaultMemoryPolicy; constexpr static MemoryPolicy sPersistentOrSystemPolicy{ .contextTimeout = 10_s, + .minimumResourceRetention = 1_s, + .maximumResourceRetention = 10_s, .useAlternativeUiHidden = true, + .purgeScratchOnly = false, }; constexpr static MemoryPolicy sLowRamPolicy{ .useAlternativeUiHidden = true, diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h index 347daf34f52a..e10dda990dec 100644 --- a/libs/hwui/MemoryPolicy.h +++ b/libs/hwui/MemoryPolicy.h @@ -53,6 +53,8 @@ struct MemoryPolicy { // The minimum amount of time to hold onto items in the resource cache // The actual time used will be the max of this & when frames were actually rendered nsecs_t minimumResourceRetention = 10_s; + // The maximum amount of time to hold onto items in the resource cache + nsecs_t maximumResourceRetention = 100000_s; // If false, use only TRIM_UI_HIDDEN to drive background cache limits; // If true, use all signals (such as all contexts are stopped) to drive the limits bool useAlternativeUiHidden = true; diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h index 13e3c8e7bf77..69fda34afc78 100644 --- a/libs/hwui/Mesh.h +++ b/libs/hwui/Mesh.h @@ -19,6 +19,7 @@ #include <GrDirectContext.h> #include <SkMesh.h> +#include <include/gpu/ganesh/SkMeshGanesh.h> #include <jni.h> #include <log/log.h> @@ -143,21 +144,34 @@ public: } if (mIsDirty || genId != mGenerationId) { - auto vb = SkMesh::MakeVertexBuffer( - context, reinterpret_cast<const void*>(mVertexBufferData.data()), - mVertexBufferData.size()); + auto vertexData = reinterpret_cast<const void*>(mVertexBufferData.data()); +#ifdef __ANDROID__ + auto vb = SkMeshes::MakeVertexBuffer(context, + vertexData, + mVertexBufferData.size()); +#else + auto vb = SkMeshes::MakeVertexBuffer(vertexData, + mVertexBufferData.size()); +#endif auto meshMode = SkMesh::Mode(mMode); if (!mIndexBufferData.empty()) { - auto ib = SkMesh::MakeIndexBuffer( - context, reinterpret_cast<const void*>(mIndexBufferData.data()), - mIndexBufferData.size()); + auto indexData = reinterpret_cast<const void*>(mIndexBufferData.data()); +#ifdef __ANDROID__ + auto ib = SkMeshes::MakeIndexBuffer(context, + indexData, + mIndexBufferData.size()); +#else + auto ib = SkMeshes::MakeIndexBuffer(indexData, + mIndexBufferData.size()); +#endif mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, ib, mIndexCount, mIndexOffset, mBuilder->fUniforms, - mBounds) + SkSpan<SkRuntimeEffect::ChildPtr>(), mBounds) .mesh; } else { mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, - mBuilder->fUniforms, mBounds) + mBuilder->fUniforms, SkSpan<SkRuntimeEffect::ChildPtr>(), + mBounds) .mesh; } mIsDirty = false; diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 06aed63d8def..5e5eb4a51b35 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -20,7 +20,7 @@ #ifdef __ANDROID__ #include "HWUIProperties.sysprop.h" #endif -#include "SkTraceEventCommon.h" +#include "src/core/SkTraceEventCommon.h" #include <algorithm> #include <cstdlib> diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 045de35c1d97..afe4c3896ed2 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -21,6 +21,7 @@ #include <SkCanvas.h> #include <SkColorSpace.h> #include <SkImage.h> +#include <SkImageAndroid.h> #include <SkImageInfo.h> #include <SkMatrix.h> #include <SkPaint.h> @@ -29,6 +30,7 @@ #include <SkSamplingOptions.h> #include <SkSurface.h> #include "include/gpu/GpuTypes.h" // from Skia +#include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <gui/TraceUtils.h> #include <private/android/AHardwareBufferHelpers.h> #include <shaders/shaders.h> @@ -108,7 +110,8 @@ void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<Copy sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(static_cast<android_dataspace>(dataspace)); sk_sp<SkImage> image = - SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace); + SkImages::DeferredFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, + colorSpace); if (!image.get()) { return request->onCopyFinished(CopyResult::UnknownError); @@ -171,16 +174,16 @@ void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<Copy SkBitmap skBitmap = request->getDestinationBitmap(srcRect.width(), srcRect.height()); SkBitmap* bitmap = &skBitmap; sk_sp<SkSurface> tmpSurface = - SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), skgpu::Budgeted::kYes, - bitmap->info(), 0, kTopLeft_GrSurfaceOrigin, nullptr); + SkSurfaces::RenderTarget(mRenderThread.getGrContext(), skgpu::Budgeted::kYes, + bitmap->info(), 0, kTopLeft_GrSurfaceOrigin, nullptr); // if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we // attempt to do the intermediate rendering step in 8888 if (!tmpSurface.get()) { SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType); - tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), - skgpu::Budgeted::kYes, - tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr); + tmpSurface = SkSurfaces::RenderTarget(mRenderThread.getGrContext(), + skgpu::Budgeted::kYes, + tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr); if (!tmpSurface.get()) { ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap"); return request->onCopyFinished(CopyResult::UnknownError); @@ -346,19 +349,19 @@ bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* * a scaling issue (b/62262733) that was encountered when sampling from an EGLImage into a * software buffer. */ - sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), - skgpu::Budgeted::kYes, - bitmap->info(), - 0, - kTopLeft_GrSurfaceOrigin, nullptr); + sk_sp<SkSurface> tmpSurface = SkSurfaces::RenderTarget(mRenderThread.getGrContext(), + skgpu::Budgeted::kYes, + bitmap->info(), + 0, + kTopLeft_GrSurfaceOrigin, nullptr); // if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we // attempt to do the intermediate rendering step in 8888 if (!tmpSurface.get()) { SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType); - tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), - skgpu::Budgeted::kYes, - tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr); + tmpSurface = SkSurfaces::RenderTarget(mRenderThread.getGrContext(), + skgpu::Budgeted::kYes, + tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr); if (!tmpSurface.get()) { ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap"); return false; diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 924fbd659824..ff0d8d74831c 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -36,6 +36,7 @@ #include "SkImageFilter.h" #include "SkImageInfo.h" #include "SkLatticeIter.h" +#include "SkMesh.h" #include "SkPaint.h" #include "SkPicture.h" #include "SkRRect.h" @@ -49,6 +50,7 @@ #include "effects/GainmapRenderer.h" #include "include/gpu/GpuTypes.h" // from Skia #include "include/gpu/GrDirectContext.h" +#include "include/gpu/ganesh/SkMeshGanesh.h" #include "pipeline/skia/AnimatedDrawables.h" #include "pipeline/skia/FunctorDrawable.h" #ifdef __ANDROID__ @@ -107,11 +109,6 @@ struct Op { }; static_assert(sizeof(Op) == 4, ""); -struct Flush final : Op { - static const auto kType = Type::Flush; - void draw(SkCanvas* c, const SkMatrix&) const { c->flush(); } -}; - struct Save final : Op { static const auto kType = Type::Save; void draw(SkCanvas* c, const SkMatrix&) const { c->save(); } @@ -532,24 +529,26 @@ struct DrawSkMesh final : Op { mutable bool isGpuBased; mutable GrDirectContext::DirectContextID contextId; void draw(SkCanvas* c, const SkMatrix&) const { +#ifdef __ANDROID__ GrDirectContext* directContext = c->recordingContext()->asDirectContext(); GrDirectContext::DirectContextID id = directContext->directContextID(); if (!isGpuBased || contextId != id) { sk_sp<SkMesh::VertexBuffer> vb = - SkMesh::CopyVertexBuffer(directContext, cpuMesh.refVertexBuffer()); + SkMeshes::CopyVertexBuffer(directContext, cpuMesh.refVertexBuffer()); if (!cpuMesh.indexBuffer()) { gpuMesh = SkMesh::Make(cpuMesh.refSpec(), cpuMesh.mode(), vb, cpuMesh.vertexCount(), cpuMesh.vertexOffset(), cpuMesh.refUniforms(), - cpuMesh.bounds()) + SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds()) .mesh; } else { sk_sp<SkMesh::IndexBuffer> ib = - SkMesh::CopyIndexBuffer(directContext, cpuMesh.refIndexBuffer()); + SkMeshes::CopyIndexBuffer(directContext, cpuMesh.refIndexBuffer()); gpuMesh = SkMesh::MakeIndexed(cpuMesh.refSpec(), cpuMesh.mode(), vb, cpuMesh.vertexCount(), cpuMesh.vertexOffset(), ib, cpuMesh.indexCount(), cpuMesh.indexOffset(), - cpuMesh.refUniforms(), cpuMesh.bounds()) + cpuMesh.refUniforms(), + SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds()) .mesh; } @@ -558,6 +557,9 @@ struct DrawSkMesh final : Op { } c->drawMesh(gpuMesh, blender, paint); +#else + c->drawMesh(cpuMesh, blender, paint); +#endif } }; @@ -675,12 +677,11 @@ public: // because the webview functor still doesn't respect the canvas clip stack. const SkIRect deviceBounds = c->getDeviceClipBounds(); if (mLayerSurface == nullptr || c->imageInfo() != mLayerImageInfo) { - GrRecordingContext* directContext = c->recordingContext(); mLayerImageInfo = c->imageInfo().makeWH(deviceBounds.width(), deviceBounds.height()); - mLayerSurface = SkSurface::MakeRenderTarget(directContext, skgpu::Budgeted::kYes, - mLayerImageInfo, 0, - kTopLeft_GrSurfaceOrigin, nullptr); + // SkCanvas::makeSurface returns a new surface that will be GPU-backed if + // canvas was also. + mLayerSurface = c->makeSurface(mLayerImageInfo); } SkCanvas* layerCanvas = mLayerSurface->getCanvas(); @@ -752,10 +753,6 @@ inline void DisplayListData::map(const Fn fns[], Args... args) const { } } -void DisplayListData::flush() { - this->push<Flush>(0); -} - void DisplayListData::save() { this->push<Save>(0); } @@ -1047,10 +1044,6 @@ sk_sp<SkSurface> RecordingCanvas::onNewSurface(const SkImageInfo&, const SkSurfa return nullptr; } -void RecordingCanvas::onFlush() { - fDL->flush(); -} - void RecordingCanvas::willSave() { mSaveCount++; fDL->save(); diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 1f4ba5d6d557..4f54ee286a56 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -127,8 +127,6 @@ public: private: friend class RecordingCanvas; - void flush(); - void save(); void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, SkCanvas::SaveLayerFlags); void saveBehind(const SkRect*); @@ -208,8 +206,6 @@ public: void willRestore() override; bool onDoSaveBehind(const SkRect*) override; - void onFlush() override; - void didConcat44(const SkM44&) override; void didSetM44(const SkM44&) override; void didScale(SkScalar, SkScalar) override; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 1c39db3a31bb..d28bb499c907 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -28,16 +28,20 @@ #include "DamageAccumulator.h" #include "pipeline/skia/SkiaDisplayList.h" #endif +#include <SkPathOps.h> #include <gui/TraceUtils.h> -#include "utils/MathUtils.h" -#include "utils/StringUtils.h" +#include <ui/FatVector.h> -#include <SkPathOps.h> #include <algorithm> #include <atomic> #include <sstream> #include <string> -#include <ui/FatVector.h> + +#ifdef __ANDROID__ +#include "include/gpu/ganesh/SkImageGanesh.h" +#endif +#include "utils/MathUtils.h" +#include "utils/StringUtils.h" namespace android { namespace uirenderer { @@ -109,6 +113,13 @@ void RenderNode::output(std::ostream& output, uint32_t level) { output << std::endl; } +void RenderNode::visit(std::function<void(const RenderNode&)> func) const { + func(*this); + if (mDisplayList) { + mDisplayList.visit(func); + } +} + int RenderNode::getUsageSize() { int size = sizeof(RenderNode); size += mStagingDisplayList.getUsedSize(); @@ -260,6 +271,12 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu pushStagingDisplayListChanges(observer, info); } + // always damageSelf when filtering backdrop content, or else the BackdropFilterDrawable will + // get a wrong snapshot of previous content. + if (mProperties.layerProperties().getBackdropImageFilter()) { + damageSelf(info); + } + if (mDisplayList) { info.out.hasFunctors |= mDisplayList.hasFunctor(); mHasHolePunches = mDisplayList.hasHolePunches(); @@ -357,13 +374,18 @@ std::optional<RenderNode::SnapshotResult> RenderNode::updateSnapshotIfRequired( mImageFilterClipBounds != clipBounds || mTargetImageFilterLayerSurfaceGenerationId != layerSurfaceGenerationId) { // Otherwise create a new snapshot with the given filter and snapshot - mSnapshotResult.snapshot = - snapshot->makeWithFilter(context, - imageFilter, - subset, - clipBounds, - &mSnapshotResult.outSubset, - &mSnapshotResult.outOffset); +#ifdef __ANDROID__ + if (context) { + mSnapshotResult.snapshot = SkImages::MakeWithFilter( + context, snapshot, imageFilter, subset, clipBounds, &mSnapshotResult.outSubset, + &mSnapshotResult.outOffset); + } else +#endif + { + mSnapshotResult.snapshot = SkImages::MakeWithFilter( + snapshot, imageFilter, subset, clipBounds, &mSnapshotResult.outSubset, + &mSnapshotResult.outOffset); + } mTargetImageFilter = sk_ref_sp(imageFilter); mImageFilterClipBounds = clipBounds; mTargetImageFilterLayerSurfaceGenerationId = layerSurfaceGenerationId; diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 572635a9bd45..c959db37474b 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -129,10 +129,6 @@ public: StretchMask& getStretchMask() { return mStretchMask; } - VirtualLightRefBase* getUserContext() const { return mUserContext.get(); } - - void setUserContext(VirtualLightRefBase* context) { mUserContext = context; } - bool isPropertyFieldDirty(DirtyPropertyMask field) const { return mDirtyPropertyFields & field; } @@ -215,6 +211,8 @@ public: void output(std::ostream& output, uint32_t level); + void visit(std::function<void(const RenderNode&)>) const; + void setUsageHint(UsageHint usageHint) { mUsageHint = usageHint; } UsageHint usageHint() const { return mUsageHint; } @@ -222,6 +220,7 @@ public: int64_t uniqueId() const { return mUniqueId; } void setIsTextureView() { mIsTextureView = true; } + bool isTextureView() const { return mIsTextureView; } void markDrawStart(SkCanvas& canvas); void markDrawEnd(SkCanvas& canvas); @@ -248,7 +247,6 @@ private: const int64_t mUniqueId; String8 mName; - sp<VirtualLightRefBase> mUserContext; uint32_t mDirtyPropertyFields; RenderProperties mProperties; diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 0589f136b666..c5371236b9cf 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -55,6 +55,12 @@ bool LayerProperties::setImageFilter(SkImageFilter* imageFilter) { return true; } +bool LayerProperties::setBackdropImageFilter(SkImageFilter* imageFilter) { + if (mBackdropImageFilter.get() == imageFilter) return false; + mBackdropImageFilter = sk_ref_sp(imageFilter); + return true; +} + bool LayerProperties::setFromPaint(const SkPaint* paint) { bool changed = false; changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint))); @@ -70,6 +76,7 @@ LayerProperties& LayerProperties::operator=(const LayerProperties& other) { setXferMode(other.xferMode()); setColorFilter(other.getColorFilter()); setImageFilter(other.getImageFilter()); + setBackdropImageFilter(other.getBackdropImageFilter()); mStretchEffect = other.mStretchEffect; return *this; } diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 064ba7aee107..e358b57f6fe1 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -97,8 +97,12 @@ public: bool setImageFilter(SkImageFilter* imageFilter); + bool setBackdropImageFilter(SkImageFilter* imageFilter); + SkImageFilter* getImageFilter() const { return mImageFilter.get(); } + SkImageFilter* getBackdropImageFilter() const { return mBackdropImageFilter.get(); } + const StretchEffect& getStretchEffect() const { return mStretchEffect; } StretchEffect& mutableStretchEffect() { return mStretchEffect; } @@ -129,6 +133,7 @@ private: SkBlendMode mMode; sk_sp<SkColorFilter> mColorFilter; sk_sp<SkImageFilter> mImageFilter; + sk_sp<SkImageFilter> mBackdropImageFilter; StretchEffect mStretchEffect; }; diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 8394c3cd4175..31fc929dfcdf 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -47,6 +47,7 @@ #include <utility> #include "CanvasProperty.h" +#include "FeatureFlags.h" #include "Mesh.h" #include "NinePatchUtils.h" #include "VectorDrawable.h" @@ -795,7 +796,9 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& pai sk_sp<SkTextBlob> textBlob(builder.make()); applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); }); - drawTextDecorations(x, y, totalAdvance, paintCopy); + if (!text_feature::fix_double_underline()) { + drawTextDecorations(x, y, totalAdvance, paintCopy); + } } void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index b785989f35cb..ced02241ffe2 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -175,7 +175,7 @@ protected: const Paint& paint, const SkPath& path, size_t start, size_t end) override; - void onFilterPaint(Paint& paint); + virtual void onFilterPaint(Paint& paint); Paint filterPaint(const Paint& src) { Paint dst(src); diff --git a/libs/hwui/SkiaInterpolator.cpp b/libs/hwui/SkiaInterpolator.cpp index b58f517834a3..c67b135855f7 100644 --- a/libs/hwui/SkiaInterpolator.cpp +++ b/libs/hwui/SkiaInterpolator.cpp @@ -18,9 +18,8 @@ #include "include/core/SkScalar.h" #include "include/core/SkTypes.h" -#include "include/private/SkFixed.h" -#include "src/core/SkTSearch.h" +#include <cstdlib> #include <log/log.h> typedef int Dot14; @@ -41,18 +40,18 @@ static inline Dot14 pin_and_convert(float x) { if (x <= 0) { return 0; } - if (x >= SK_Scalar1) { + if (x >= 1.0f) { return Dot14_ONE; } - return SkScalarToFixed(x) >> 2; + return static_cast<Dot14>(x * Dot14_ONE); } static float SkUnitCubicInterp(float value, float bx, float by, float cx, float cy) { // pin to the unit-square, and convert to 2.14 Dot14 x = pin_and_convert(value); - if (x == 0) return 0; - if (x == Dot14_ONE) return SK_Scalar1; + if (x == 0) return 0.0f; + if (x == Dot14_ONE) return 1.0f; Dot14 b = pin_and_convert(bx); Dot14 c = pin_and_convert(cx); @@ -84,7 +83,7 @@ static float SkUnitCubicInterp(float value, float bx, float by, float cx, float A = 3 * b; B = 3 * (c - 2 * b); C = 3 * (b - c) + Dot14_ONE; - return SkFixedToScalar(eval_cubic(t, A, B, C) << 2); + return Dot14ToFloat(eval_cubic(t, A, B, C)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -104,7 +103,7 @@ void SkiaInterpolatorBase::reset(int elemCount, int frameCount) { fFlags = 0; fElemCount = static_cast<uint8_t>(elemCount); fFrameCount = static_cast<int16_t>(frameCount); - fRepeat = SK_Scalar1; + fRepeat = 1.0f; if (fStorage) { free(fStorage); fStorage = nullptr; @@ -136,17 +135,46 @@ bool SkiaInterpolatorBase::getDuration(SkMSec* startTime, SkMSec* endTime) const float SkiaInterpolatorBase::ComputeRelativeT(SkMSec time, SkMSec prevTime, SkMSec nextTime, const float blend[4]) { - SkASSERT(time > prevTime && time < nextTime); + LOG_FATAL_IF(time < prevTime || time > nextTime); float t = (float)(time - prevTime) / (float)(nextTime - prevTime); return blend ? SkUnitCubicInterp(t, blend[0], blend[1], blend[2], blend[3]) : t; } +// Returns the index of where the item is or the bit not of the index +// where the item should go in order to keep arr sorted in ascending order. +int SkiaInterpolatorBase::binarySearch(const SkTimeCode* arr, int count, SkMSec target) { + if (count <= 0) { + return ~0; + } + + int lo = 0; + int hi = count - 1; + + while (lo < hi) { + int mid = (hi + lo) / 2; + SkMSec elem = arr[mid].fTime; + if (elem == target) { + return mid; + } else if (elem < target) { + lo = mid + 1; + } else { + hi = mid; + } + } + // Check to see if target is greater or less than where we stopped + if (target < arr[lo].fTime) { + return ~lo; + } + // e.g. it should go at the end. + return ~(lo + 1); +} + SkiaInterpolatorBase::Result SkiaInterpolatorBase::timeToT(SkMSec time, float* T, int* indexPtr, bool* exactPtr) const { - SkASSERT(fFrameCount > 0); + LOG_FATAL_IF(fFrameCount <= 0); Result result = kNormal_Result; - if (fRepeat != SK_Scalar1) { + if (fRepeat != 1.0f) { SkMSec startTime = 0, endTime = 0; // initialize to avoid warning this->getDuration(&startTime, &endTime); SkMSec totalTime = endTime - startTime; @@ -168,10 +196,8 @@ SkiaInterpolatorBase::Result SkiaInterpolatorBase::timeToT(SkMSec time, float* T time = offsetTime + startTime; } - int index = SkTSearch<SkMSec>(&fTimes[0].fTime, fFrameCount, time, sizeof(SkTimeCode)); - + int index = SkiaInterpolatorBase::binarySearch(fTimes, fFrameCount, time); bool exact = true; - if (index < 0) { index = ~index; if (index == 0) { @@ -184,10 +210,11 @@ SkiaInterpolatorBase::Result SkiaInterpolatorBase::timeToT(SkMSec time, float* T } result = kFreezeEnd_Result; } else { + // Need to interpolate between two frames. exact = false; } } - SkASSERT(index < fFrameCount); + LOG_FATAL_IF(index >= fFrameCount); const SkTimeCode* nextTime = &fTimes[index]; SkMSec nextT = nextTime[0].fTime; if (exact) { @@ -207,7 +234,7 @@ SkiaInterpolator::SkiaInterpolator() { } SkiaInterpolator::SkiaInterpolator(int elemCount, int frameCount) { - SkASSERT(elemCount > 0); + LOG_FATAL_IF(elemCount <= 0); this->reset(elemCount, frameCount); } @@ -221,21 +248,19 @@ void SkiaInterpolator::reset(int elemCount, int frameCount) { fValues = (float*)((char*)fStorage + sizeof(SkTimeCode) * frameCount); } -#define SK_Fixed1Third (SK_Fixed1 / 3) -#define SK_Fixed2Third (SK_Fixed1 * 2 / 3) - static const float gIdentityBlend[4] = {0.33333333f, 0.33333333f, 0.66666667f, 0.66666667f}; bool SkiaInterpolator::setKeyFrame(int index, SkMSec time, const float values[], const float blend[4]) { - SkASSERT(values != nullptr); + LOG_FATAL_IF(values == nullptr); if (blend == nullptr) { blend = gIdentityBlend; } - bool success = ~index == SkTSearch<SkMSec>(&fTimes->fTime, index, time, sizeof(SkTimeCode)); - SkASSERT(success); + // Verify the time should go after all the frames before index + bool success = ~index == SkiaInterpolatorBase::binarySearch(fTimes, index, time); + LOG_FATAL_IF(!success); if (success) { SkTimeCode* timeCode = &fTimes[index]; timeCode->fTime = time; @@ -257,7 +282,7 @@ SkiaInterpolator::Result SkiaInterpolator::timeToValues(SkMSec time, float value if (exact) { memcpy(values, nextSrc, fElemCount * sizeof(float)); } else { - SkASSERT(index > 0); + LOG_FATAL_IF(index <= 0); const float* prevSrc = nextSrc - fElemCount; diff --git a/libs/hwui/SkiaInterpolator.h b/libs/hwui/SkiaInterpolator.h index 9422cb526a8f..62e6c1e33e40 100644 --- a/libs/hwui/SkiaInterpolator.h +++ b/libs/hwui/SkiaInterpolator.h @@ -68,14 +68,16 @@ protected: enum Flags { kMirror = 1, kReset = 2, kHasBlend = 4 }; static float ComputeRelativeT(uint32_t time, uint32_t prevTime, uint32_t nextTime, const float blend[4] = nullptr); - int16_t fFrameCount; - uint8_t fElemCount; - uint8_t fFlags; - float fRepeat; struct SkTimeCode { uint32_t fTime; float fBlend[4]; }; + static int binarySearch(const SkTimeCode* arr, int count, uint32_t target); + + int16_t fFrameCount; + uint8_t fElemCount; + uint8_t fFlags; + float fRepeat; SkTimeCode* fTimes; // pointer into fStorage void* fStorage; }; diff --git a/libs/hwui/SkiaWrapper.h b/libs/hwui/SkiaWrapper.h new file mode 100644 index 000000000000..bd0e35aadbb4 --- /dev/null +++ b/libs/hwui/SkiaWrapper.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SKIA_WRAPPER_H_ +#define SKIA_WRAPPER_H_ + +#include <SkRefCnt.h> +#include <utils/RefBase.h> + +namespace android::uirenderer { + +template <typename T> +class SkiaWrapper : public VirtualLightRefBase { +public: + sk_sp<T> getInstance() { + if (mInstance != nullptr && shouldDiscardInstance()) { + mInstance = nullptr; + } + + if (mInstance == nullptr) { + mInstance = createInstance(); + mGenerationId++; + } + return mInstance; + } + + virtual bool shouldDiscardInstance() const { return false; } + + void discardInstance() { mInstance = nullptr; } + + [[nodiscard]] int32_t getGenerationId() const { return mGenerationId; } + +protected: + virtual sk_sp<T> createInstance() = 0; + +private: + sk_sp<T> mInstance = nullptr; + int32_t mGenerationId = 0; +}; + +} // namespace android::uirenderer + +#endif // SKIA_WRAPPER_H_ diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt b/libs/hwui/SkippedFrameInfo.h index abc6b9f9a746..de56d9a26982 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt +++ b/libs/hwui/SkippedFrameInfo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,17 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.bubble +#pragma once -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +namespace android::uirenderer { -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class ChangeActiveActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) : - ChangeActiveActivityFromBubbleTest(flicker) +enum class SkippedFrameReason { + DrawingOff, + ContextIsStopped, + NothingToDraw, + NoOutputTarget, + NoBuffer, + AlreadyDrawn, +}; + +} /* namespace android::uirenderer */ diff --git a/libs/hwui/Tonemapper.cpp b/libs/hwui/Tonemapper.cpp index 974a5d05aa84..ae29edf535a2 100644 --- a/libs/hwui/Tonemapper.cpp +++ b/libs/hwui/Tonemapper.cpp @@ -20,6 +20,7 @@ #include <log/log.h> // libshaders only exists on Android devices #ifdef __ANDROID__ +#include <renderthread/CanvasContext.h> #include <shaders/shaders.h> #endif @@ -53,8 +54,17 @@ static sk_sp<SkColorFilter> createLinearEffectColorFilter(const shaders::LinearE ColorFilterRuntimeEffectBuilder effectBuilder(std::move(runtimeEffect)); + auto colorTransform = android::mat4(); + const auto* context = renderthread::CanvasContext::getActiveContext(); + if (context) { + const auto ratio = context->targetSdrHdrRatio(); + if (ratio > 1.0f) { + colorTransform = android::mat4::scale(vec4(ratio, ratio, ratio, 1.f)); + } + } + const auto uniforms = - shaders::buildLinearEffectUniforms(linearEffect, android::mat4(), maxDisplayLuminance, + shaders::buildLinearEffectUniforms(linearEffect, colorTransform, maxDisplayLuminance, currentDisplayLuminanceNits, maxLuminance); for (const auto& uniform : uniforms) { diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 2bff9cb74fa7..ea25f68d7170 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -16,14 +16,16 @@ #pragma once -#include "Properties.h" -#include "utils/Macros.h" - #include <utils/Timers.h> -#include "SkSize.h" +#include <optional> #include <string> +#include "Properties.h" +#include "SkSize.h" +#include "SkippedFrameInfo.h" +#include "utils/Macros.h" + namespace android { namespace uirenderer { @@ -110,13 +112,13 @@ public: // animate itself, such as if hasFunctors is true // This is only set if hasAnimations is true bool requiresUiRedraw = false; - // This is set to true if draw() can be called this frame - // false means that we must delay until the next vsync pulse as frame + // This is set to nullopt if draw() can be called this frame + // A value means that we must delay until the next vsync pulse as frame // production is outrunning consumption - // NOTE that if this is false CanvasContext will set either requiresUiRedraw + // NOTE that if this has a value CanvasContext will set either requiresUiRedraw // *OR* will post itself for the next vsync automatically, use this // only to avoid calling draw() - bool canDrawThisFrame = true; + std::optional<SkippedFrameReason> skippedFrameReason; // Sentinel for animatedImageDelay meaning there is no need to post such // a message. static constexpr nsecs_t kNoAnimatedImageDelay = -1; diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig new file mode 100644 index 000000000000..e672b983d509 --- /dev/null +++ b/libs/hwui/aconfig/hwui_flags.aconfig @@ -0,0 +1,29 @@ +package: "com.android.graphics.hwui.flags" + +flag { + name: "limited_hdr" + namespace: "core_graphics" + description: "API to enable apps to restrict the amount of HDR headroom that is used" + bug: "234181960" +} + +flag { + name: "hdr_10bit_plus" + namespace: "core_graphics" + description: "Use 10101010 and FP16 formats for HDR-UI when available" + bug: "284159488" +} + +flag { + name: "gainmap_animations" + namespace: "core_graphics" + description: "APIs to help enable animations involving gainmaps" + bug: "296482289" +} + +flag { + name: "gainmap_constructor_with_metadata" + namespace: "core_graphics" + description: "APIs to create a new gainmap with a bitmap for metadata." + bug: "304478551" +} diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp index c442a7b1d17c..c80a9b4ae97f 100644 --- a/libs/hwui/apex/android_bitmap.cpp +++ b/libs/hwui/apex/android_bitmap.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "Bitmap" #include <log/log.h> #include "android/graphics/bitmap.h" diff --git a/libs/hwui/apex/android_canvas.cpp b/libs/hwui/apex/android_canvas.cpp index 905b123076a2..19f726a31b33 100644 --- a/libs/hwui/apex/android_canvas.cpp +++ b/libs/hwui/apex/android_canvas.cpp @@ -45,9 +45,9 @@ static bool convert(const ANativeWindow_Buffer* buffer, SkImageInfo imageInfo = uirenderer::ANativeWindowToImageInfo(*buffer, cs); size_t rowBytes = buffer->stride * imageInfo.bytesPerPixel(); - // If SkSurface::MakeRasterDirect fails then we should as well as we will not be able to + // If SkSurfaces::WrapPixels fails then we should as well as we will not be able to // draw into the canvas. - sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(imageInfo, buffer->bits, rowBytes); + sk_sp<SkSurface> surface = SkSurfaces::WrapPixels(imageInfo, buffer->bits, rowBytes); if (surface.get() != nullptr) { if (outBitmap) { outBitmap->setInfo(imageInfo, rowBytes); diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index 09ae7e78fe23..883f273b5d3d 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -25,9 +25,6 @@ #include <sys/cdefs.h> #include <vulkan/vulkan.h> -#undef LOG_TAG -#define LOG_TAG "AndroidGraphicsJNI" - extern int register_android_graphics_Bitmap(JNIEnv*); extern int register_android_graphics_BitmapFactory(JNIEnv*); extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*); diff --git a/libs/hwui/effects/GainmapRenderer.cpp b/libs/hwui/effects/GainmapRenderer.cpp index 651d526511ef..3ebf7d19202d 100644 --- a/libs/hwui/effects/GainmapRenderer.cpp +++ b/libs/hwui/effects/GainmapRenderer.cpp @@ -244,11 +244,18 @@ private: // This can happen if a BitmapShader is used on multiple canvas', such as a // software + hardware canvas, which is otherwise valid as SkShader is "immutable" std::lock_guard _lock(mUniformGuard); - const float Wunclamped = (sk_float_log(targetHdrSdrRatio) - - sk_float_log(mGainmapInfo.fDisplayRatioSdr)) / - (sk_float_log(mGainmapInfo.fDisplayRatioHdr) - - sk_float_log(mGainmapInfo.fDisplayRatioSdr)); - const float W = std::max(std::min(Wunclamped, 1.f), 0.f); + // Compute the weight parameter that will be used to blend between the images. + float W = 0.f; + if (targetHdrSdrRatio > mGainmapInfo.fDisplayRatioSdr) { + if (targetHdrSdrRatio < mGainmapInfo.fDisplayRatioHdr) { + W = (sk_float_log(targetHdrSdrRatio) - + sk_float_log(mGainmapInfo.fDisplayRatioSdr)) / + (sk_float_log(mGainmapInfo.fDisplayRatioHdr) - + sk_float_log(mGainmapInfo.fDisplayRatioSdr)); + } else { + W = 1.f; + } + } mBuilder.uniform("W") = W; uniforms = mBuilder.uniforms(); } diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp index 8049dc946c9e..27773a60355a 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -111,7 +111,7 @@ AnimatedImageDrawable::Snapshot AnimatedImageDrawable::decodeNextFrame() { { std::unique_lock lock{mImageLock}; snap.mDurationMS = adjustFrameDuration(mSkAnimatedImage->decodeNextFrame()); - snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot()); + snap.mPic = mSkAnimatedImage->makePictureSnapshot(); } return snap; @@ -123,7 +123,7 @@ AnimatedImageDrawable::Snapshot AnimatedImageDrawable::reset() { { std::unique_lock lock{mImageLock}; mSkAnimatedImage->reset(); - snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot()); + snap.mPic = mSkAnimatedImage->makePictureSnapshot(); snap.mDurationMS = currentFrameDuration(); } diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 92d875bf7f1e..8344a86923ee 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -43,12 +43,15 @@ #include <SkColor.h> #include <SkEncodedImageFormat.h> #include <SkHighContrastFilter.h> -#include <SkImageEncoder.h> +#include <SkImage.h> +#include <SkImageAndroid.h> #include <SkImagePriv.h> #include <SkJpegGainmapEncoder.h> #include <SkPixmap.h> #include <SkRect.h> #include <SkStream.h> +#include <SkJpegEncoder.h> +#include <SkPngEncoder.h> #include <SkWebpEncoder.h> #include <limits> @@ -296,7 +299,8 @@ Bitmap::Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes mPixelStorage.hardware.size = AHardwareBuffer_getAllocationSize(buffer); AHardwareBuffer_acquire(buffer); setImmutable(); // HW bitmaps are always immutable - mImage = SkImage::MakeFromAHardwareBuffer(buffer, mInfo.alphaType(), mInfo.refColorSpace()); + mImage = SkImages::DeferredFromAHardwareBuffer(buffer, mInfo.alphaType(), + mInfo.refColorSpace()); } #endif @@ -407,7 +411,12 @@ sk_sp<SkImage> Bitmap::makeImage() { // Note we don't cache in this case, because the raster image holds a pointer to this Bitmap // internally and ~Bitmap won't be invoked. // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here. +#ifdef __ANDROID__ + // pinnable images are only supported with the Ganesh GPU backend compiled in. + image = SkImages::PinnableRasterFromBitmap(skiaBitmap); +#else image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode); +#endif } return image; } @@ -528,17 +537,25 @@ bool Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format, return false; } - SkEncodedImageFormat fm; switch (format) { - case JavaCompressFormat::Jpeg: - fm = SkEncodedImageFormat::kJPEG; - break; + case JavaCompressFormat::Jpeg: { + SkJpegEncoder::Options options; + options.fQuality = quality; + return SkJpegEncoder::Encode(stream, bitmap.pixmap(), options); + } case JavaCompressFormat::Png: - fm = SkEncodedImageFormat::kPNG; - break; - case JavaCompressFormat::Webp: - fm = SkEncodedImageFormat::kWEBP; - break; + return SkPngEncoder::Encode(stream, bitmap.pixmap(), {}); + case JavaCompressFormat::Webp: { + SkWebpEncoder::Options options; + if (quality >= 100) { + options.fCompression = SkWebpEncoder::Compression::kLossless; + options.fQuality = 75; // This is effort to compress + } else { + options.fCompression = SkWebpEncoder::Compression::kLossy; + options.fQuality = quality; + } + return SkWebpEncoder::Encode(stream, bitmap.pixmap(), options); + } case JavaCompressFormat::WebpLossy: case JavaCompressFormat::WebpLossless: { SkWebpEncoder::Options options; @@ -548,8 +565,6 @@ bool Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format, return SkWebpEncoder::Encode(stream, bitmap.pixmap(), options); } } - - return SkEncodeImage(stream, bitmap, fm, quality); } sp<uirenderer::Gainmap> Bitmap::gainmap() const { diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index cd8af3d933b1..80b6c0385fca 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -16,17 +16,18 @@ #include "Canvas.h" +#include <SkFontMetrics.h> +#include <SkRRect.h> + +#include "FeatureFlags.h" #include "MinikinUtils.h" #include "Paint.h" #include "Properties.h" #include "RenderNode.h" #include "Typeface.h" -#include "pipeline/skia/SkiaRecordingCanvas.h" - +#include "hwui/DrawTextFunctor.h" #include "hwui/PaintFilter.h" - -#include <SkFontMetrics.h> -#include <SkRRect.h> +#include "pipeline/skia/SkiaRecordingCanvas.h" namespace android { @@ -34,13 +35,6 @@ Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::Rende return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height); } -static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, - const Paint& paint, Canvas* canvas) { - const SkScalar strokeWidth = fmax(thickness, 1.0f); - const SkScalar bottom = top + strokeWidth; - canvas->drawRect(left, top, right, bottom, paint); -} - void Canvas::drawTextDecorations(float x, float y, float length, const Paint& paint) { // paint has already been filtered by our caller, so we can ignore any filter const bool strikeThru = paint.isStrikeThru(); @@ -72,73 +66,6 @@ void Canvas::drawTextDecorations(float x, float y, float length, const Paint& pa } } -static void simplifyPaint(int color, Paint* paint) { - paint->setColor(color); - paint->setShader(nullptr); - paint->setColorFilter(nullptr); - paint->setLooper(nullptr); - paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize()); - paint->setStrokeJoin(SkPaint::kRound_Join); - paint->setLooper(nullptr); -} - -class DrawTextFunctor { -public: - DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x, - float y, float totalAdvance) - : layout(layout) - , canvas(canvas) - , paint(paint) - , x(x) - , y(y) - , totalAdvance(totalAdvance) {} - - void operator()(size_t start, size_t end) { - auto glyphFunc = [&](uint16_t* text, float* positions) { - for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { - text[textIndex++] = layout.getGlyphId(i); - positions[posIndex++] = x + layout.getX(i); - positions[posIndex++] = y + layout.getY(i); - } - }; - - size_t glyphCount = end - start; - - if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) { - // high contrast draw path - int color = paint.getColor(); - int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color); - bool darken = channelSum < (128 * 3); - - // outline - gDrawTextBlobMode = DrawTextBlobMode::HctOutline; - Paint outlinePaint(paint); - simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); - outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); - canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance); - - // inner - gDrawTextBlobMode = DrawTextBlobMode::HctInner; - Paint innerPaint(paint); - simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); - innerPaint.setStyle(SkPaint::kFill_Style); - canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance); - gDrawTextBlobMode = DrawTextBlobMode::Normal; - } else { - // standard draw path - canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance); - } - } - -private: - const minikin::Layout& layout; - Canvas* canvas; - const Paint& paint; - float x; - float y; - float totalAdvance; -}; - void Canvas::drawGlyphs(const minikin::Font& font, const int* glyphIds, const float* positions, int glyphCount, const Paint& paint) { // Minikin modify skFont for auto-fakebold/auto-fakeitalic. @@ -151,7 +78,7 @@ void Canvas::drawGlyphs(const minikin::Font& font, const int* glyphIds, const fl memcpy(outPositions, positions, sizeof(float) * 2 * glyphCount); }; - const minikin::MinikinFont* minikinFont = font.typeface().get(); + const minikin::MinikinFont* minikinFont = font.baseTypeface().get(); SkFont* skfont = &copied.getSkFont(); MinikinFontSkia::populateSkFont(skfont, minikinFont, minikin::FontFakery()); @@ -182,6 +109,31 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance()); MinikinUtils::forFontRun(layout, &paint, f); + + if (text_feature::fix_double_underline()) { + Paint copied(paint); + PaintFilter* filter = getPaintFilter(); + if (filter != nullptr) { + filter->filterFullPaint(&copied); + } + const bool isUnderline = copied.isUnderline(); + const bool isStrikeThru = copied.isStrikeThru(); + if (isUnderline || isStrikeThru) { + const SkScalar left = x; + const SkScalar right = x + layout.getAdvance(); + if (isUnderline) { + const SkScalar top = y + f.getUnderlinePosition(); + drawStroke(left, right, top, f.getUnderlineThickness(), copied, this); + } + if (isStrikeThru) { + float textSize = paint.getSkFont().getSize(); + const float position = textSize * Paint::kStdStrikeThru_Top; + const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness; + const SkScalar top = y + position; + drawStroke(left, right, top, thickness, copied, this); + } + } + } } void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight, diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 44ee31d34d23..9ec023b2c36f 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -285,7 +285,7 @@ protected: * totalAdvance: used to define width of text decorations (underlines, strikethroughs). */ virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x, - float y,float totalAdvance) = 0; + float y, float totalAdvance) = 0; virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, const Paint& paint, const SkPath& path, size_t start, size_t end) = 0; diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h new file mode 100644 index 000000000000..2e6e97634aec --- /dev/null +++ b/libs/hwui/hwui/DrawTextFunctor.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <SkFontMetrics.h> +#include <SkRRect.h> + +#include "Canvas.h" +#include "FeatureFlags.h" +#include "MinikinUtils.h" +#include "Paint.h" +#include "Properties.h" +#include "RenderNode.h" +#include "Typeface.h" +#include "hwui/PaintFilter.h" +#include "pipeline/skia/SkiaRecordingCanvas.h" + +namespace android { + +static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, + const Paint& paint, Canvas* canvas) { + const SkScalar strokeWidth = fmax(thickness, 1.0f); + const SkScalar bottom = top + strokeWidth; + canvas->drawRect(left, top, right, bottom, paint); +} + +static void simplifyPaint(int color, Paint* paint) { + paint->setColor(color); + paint->setShader(nullptr); + paint->setColorFilter(nullptr); + paint->setLooper(nullptr); + paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize()); + paint->setStrokeJoin(SkPaint::kRound_Join); + paint->setLooper(nullptr); +} + +class DrawTextFunctor { +public: + DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x, + float y, float totalAdvance) + : layout(layout) + , canvas(canvas) + , paint(paint) + , x(x) + , y(y) + , totalAdvance(totalAdvance) + , underlinePosition(0) + , underlineThickness(0) {} + + void operator()(size_t start, size_t end) { + auto glyphFunc = [&](uint16_t* text, float* positions) { + for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { + text[textIndex++] = layout.getGlyphId(i); + positions[posIndex++] = x + layout.getX(i); + positions[posIndex++] = y + layout.getY(i); + } + }; + + size_t glyphCount = end - start; + + if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) { + // high contrast draw path + int color = paint.getColor(); + int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color); + bool darken = channelSum < (128 * 3); + + // outline + gDrawTextBlobMode = DrawTextBlobMode::HctOutline; + Paint outlinePaint(paint); + simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); + outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); + canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance); + + // inner + gDrawTextBlobMode = DrawTextBlobMode::HctInner; + Paint innerPaint(paint); + simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); + innerPaint.setStyle(SkPaint::kFill_Style); + canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance); + gDrawTextBlobMode = DrawTextBlobMode::Normal; + } else { + // standard draw path + canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance); + } + + if (text_feature::fix_double_underline()) { + // Extract underline position and thickness. + if (paint.isUnderline()) { + SkFontMetrics metrics; + paint.getSkFont().getMetrics(&metrics); + const float textSize = paint.getSkFont().getSize(); + SkScalar position; + if (!metrics.hasUnderlinePosition(&position)) { + position = textSize * Paint::kStdUnderline_Top; + } + SkScalar thickness; + if (!metrics.hasUnderlineThickness(&thickness)) { + thickness = textSize * Paint::kStdUnderline_Thickness; + } + + // If multiple fonts are used, use the most bottom position and most thick stroke + // width as the underline position. This follows the CSS standard: + // https://www.w3.org/TR/css-text-decor-3/#text-underline-position-property + // <quote> + // The exact position and thickness of line decorations is UA-defined in this level. + // However, for underlines and overlines the UA must use a single thickness and + // position on each line for the decorations deriving from a single decorating box. + // </quote> + underlinePosition = std::max(underlinePosition, position); + underlineThickness = std::max(underlineThickness, thickness); + } + } + } + + float getUnderlinePosition() const { return underlinePosition; } + float getUnderlineThickness() const { return underlineThickness; } + +private: + const minikin::Layout& layout; + Canvas* canvas; + const Paint& paint; + float x; + float y; + float totalAdvance; + float underlinePosition; + float underlineThickness; +}; + +} // namespace android diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index 701a87f0cce4..588463c49497 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -43,9 +43,6 @@ #include <memory> -#undef LOG_TAG -#define LOG_TAG "ImageDecoder" - using namespace android; sk_sp<SkColorSpace> ImageDecoder::getDefaultColorSpace() const { diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index e359145feef7..bcfb4c89036d 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -84,7 +84,8 @@ void MinikinUtils::getBounds(const Paint* paint, minikin::Bidi bidiFlags, const float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, size_t start, - size_t count, size_t bufSize, float* advances) { + size_t count, size_t bufSize, float* advances, + minikin::MinikinRect* bounds) { minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface); const minikin::U16StringPiece textBuf(buf, bufSize); const minikin::Range range(start, start + count); @@ -92,7 +93,7 @@ float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags, const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit(); return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen, - endHyphen, advances); + endHyphen, advances, bounds); } minikin::MinikinExtent MinikinUtils::getFontExtent(const Paint* paint, minikin::Bidi bidiFlags, diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h index 009b84b140ea..61bc881faa54 100644 --- a/libs/hwui/hwui/MinikinUtils.h +++ b/libs/hwui/hwui/MinikinUtils.h @@ -51,10 +51,9 @@ public: static void getBounds(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, size_t bufSize, minikin::MinikinRect* out); - static float measureText(const Paint* paint, minikin::Bidi bidiFlags, - const Typeface* typeface, const uint16_t* buf, - size_t start, size_t count, size_t bufSize, - float* advances); + static float measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, + const uint16_t* buf, size_t start, size_t count, size_t bufSize, + float* advances, minikin::MinikinRect* bounds); static minikin::MinikinExtent getFontExtent(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, @@ -76,7 +75,7 @@ public: size_t start = 0; size_t nGlyphs = layout.nGlyphs(); for (size_t i = 0; i < nGlyphs; i++) { - const minikin::MinikinFont* nextFont = layout.getFont(i)->typeface().get(); + const minikin::MinikinFont* nextFont = layout.typeface(i).get(); if (i > 0 && nextFont != curFont) { SkFont* skfont = &paint->getSkFont(); MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start)); diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index 3c67edc9a428..b63ee1bd3d98 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -140,9 +140,8 @@ Typeface* Typeface::createFromFamilies(std::vector<std::shared_ptr<minikin::Font const minikin::FontStyle defaultStyle; const minikin::MinikinFont* mf = - families.empty() - ? nullptr - : families[0]->getClosestMatch(defaultStyle).font->typeface().get(); + families.empty() ? nullptr + : families[0]->getClosestMatch(defaultStyle).typeface().get(); if (mf != nullptr) { SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(mf)->GetSkTypeface(); const SkFontStyle& style = skTypeface->fontStyle(); diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp index a7f5aa83e624..90b1da846205 100644 --- a/libs/hwui/jni/AnimatedImageDrawable.cpp +++ b/libs/hwui/jni/AnimatedImageDrawable.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -#include "GraphicsJNI.h" -#include "ImageDecoder.h" -#include "Utils.h" - #include <SkAndroidCodec.h> #include <SkAnimatedImage.h> #include <SkColorFilter.h> @@ -27,10 +23,15 @@ #include <SkRect.h> #include <SkRefCnt.h> #include <hwui/AnimatedImageDrawable.h> -#include <hwui/ImageDecoder.h> #include <hwui/Canvas.h> +#include <hwui/ImageDecoder.h> #include <utils/Looper.h> +#include "ColorFilter.h" +#include "GraphicsJNI.h" +#include "ImageDecoder.h" +#include "Utils.h" + using namespace android; static jclass gAnimatedImageDrawableClass; @@ -145,8 +146,9 @@ static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlo static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jlong nativeFilter) { auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); - auto* filter = reinterpret_cast<SkColorFilter*>(nativeFilter); - drawable->setStagingColorFilter(sk_ref_sp(filter)); + auto filter = uirenderer::ColorFilter::fromJava(nativeFilter); + auto skColorFilter = filter != nullptr ? filter->getInstance() : sk_sp<SkColorFilter>(); + drawable->setStagingColorFilter(skColorFilter); } static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index 6ee7576651f2..9e21f860ce21 100644 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -1,5 +1,3 @@ -#undef LOG_TAG -#define LOG_TAG "Bitmap" // #define LOG_NDEBUG 0 #include "Bitmap.h" diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index 8abcd9a59122..3d0a53440bfb 100644 --- a/libs/hwui/jni/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -1,6 +1,3 @@ -#undef LOG_TAG -#define LOG_TAG "BitmapFactory" - #include "BitmapFactory.h" #include <Gainmap.h> diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp index 740988f77270..ea5c14486ea4 100644 --- a/libs/hwui/jni/BitmapRegionDecoder.cpp +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "BitmapRegionDecoder" - #include "BitmapRegionDecoder.h" #include <HardwareBitmapUploader.h> diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp index 4bd7ef47b871..0b95148d3c82 100644 --- a/libs/hwui/jni/ColorFilter.cpp +++ b/libs/hwui/jni/ColorFilter.cpp @@ -15,20 +15,21 @@ ** limitations under the License. */ -#include "GraphicsJNI.h" +#include "ColorFilter.h" +#include "GraphicsJNI.h" #include "SkBlendMode.h" -#include "SkColorFilter.h" -#include "SkColorMatrixFilter.h" namespace android { using namespace uirenderer; -class SkColorFilterGlue { +class ColorFilterGlue { public: - static void SafeUnref(SkColorFilter* filter) { - SkSafeUnref(filter); + static void SafeUnref(ColorFilter* filter) { + if (filter) { + filter->decStrong(nullptr); + } } static jlong GetNativeFinalizer(JNIEnv*, jobject) { @@ -36,41 +37,75 @@ public: } static jlong CreateBlendModeFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) { - SkBlendMode mode = static_cast<SkBlendMode>(modeHandle); - return reinterpret_cast<jlong>(SkColorFilters::Blend(srcColor, mode).release()); + auto mode = static_cast<SkBlendMode>(modeHandle); + auto* blendModeFilter = new BlendModeColorFilter(srcColor, mode); + blendModeFilter->incStrong(nullptr); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(blendModeFilter)); } static jlong CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) { - return reinterpret_cast<jlong>(SkColorMatrixFilter::MakeLightingFilter(mul, add).release()); + auto* lightingFilter = new LightingFilter(mul, add); + lightingFilter->incStrong(nullptr); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(lightingFilter)); } - static jlong CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) { - float matrix[20]; - env->GetFloatArrayRegion(jarray, 0, 20, matrix); + static void SetLightingFilterMul(JNIEnv* env, jobject, jlong lightingFilterPtr, jint mul) { + auto* filter = reinterpret_cast<LightingFilter*>(lightingFilterPtr); + if (filter) { + filter->setMul(mul); + } + } + + static void SetLightingFilterAdd(JNIEnv* env, jobject, jlong lightingFilterPtr, jint add) { + auto* filter = reinterpret_cast<LightingFilter*>(lightingFilterPtr); + if (filter) { + filter->setAdd(add); + } + } + + static std::vector<float> getMatrixFromJFloatArray(JNIEnv* env, jfloatArray jarray) { + std::vector<float> matrix(20); + // float matrix[20]; + env->GetFloatArrayRegion(jarray, 0, 20, matrix.data()); // java biases the translates by 255, so undo that before calling skia matrix[ 4] *= (1.0f/255); matrix[ 9] *= (1.0f/255); matrix[14] *= (1.0f/255); matrix[19] *= (1.0f/255); - return reinterpret_cast<jlong>(SkColorFilters::Matrix(matrix).release()); + return matrix; + } + + static jlong CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) { + std::vector<float> matrix = getMatrixFromJFloatArray(env, jarray); + auto* colorMatrixColorFilter = new ColorMatrixColorFilter(std::move(matrix)); + colorMatrixColorFilter->incStrong(nullptr); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(colorMatrixColorFilter)); + } + + static void SetColorMatrix(JNIEnv* env, jobject, jlong colorMatrixColorFilterPtr, + jfloatArray jarray) { + auto* filter = reinterpret_cast<ColorMatrixColorFilter*>(colorMatrixColorFilterPtr); + if (filter) { + filter->setMatrix(getMatrixFromJFloatArray(env, jarray)); + } } }; static const JNINativeMethod colorfilter_methods[] = { - {"nativeGetFinalizer", "()J", (void*) SkColorFilterGlue::GetNativeFinalizer } -}; + {"nativeGetFinalizer", "()J", (void*)ColorFilterGlue::GetNativeFinalizer}}; static const JNINativeMethod blendmode_methods[] = { - { "native_CreateBlendModeFilter", "(II)J", (void*) SkColorFilterGlue::CreateBlendModeFilter }, + {"native_CreateBlendModeFilter", "(II)J", (void*)ColorFilterGlue::CreateBlendModeFilter}, }; static const JNINativeMethod lighting_methods[] = { - { "native_CreateLightingFilter", "(II)J", (void*) SkColorFilterGlue::CreateLightingFilter }, -}; + {"native_CreateLightingFilter", "(II)J", (void*)ColorFilterGlue::CreateLightingFilter}, + {"native_SetLightingFilterAdd", "(JI)V", (void*)ColorFilterGlue::SetLightingFilterAdd}, + {"native_SetLightingFilterMul", "(JI)V", (void*)ColorFilterGlue::SetLightingFilterMul}}; static const JNINativeMethod colormatrix_methods[] = { - { "nativeColorMatrixFilter", "([F)J", (void*) SkColorFilterGlue::CreateColorMatrixFilter }, -}; + {"nativeColorMatrixFilter", "([F)J", (void*)ColorFilterGlue::CreateColorMatrixFilter}, + {"nativeSetColorMatrix", "(J[F)V", (void*)ColorFilterGlue::SetColorMatrix}}; int register_android_graphics_ColorFilter(JNIEnv* env) { android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods, diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp index 15e529e169fc..a66d3b860ade 100644 --- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp @@ -1,11 +1,11 @@ #include "CreateJavaOutputStreamAdaptor.h" #include "SkData.h" -#include "SkMalloc.h" #include "SkRefCnt.h" #include "SkStream.h" #include "SkTypes.h" #include "Utils.h" +#include <cstdlib> #include <nativehelper/JNIHelp.h> #include <log/log.h> #include <memory> @@ -177,6 +177,10 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray s return JavaInputStreamAdaptor::Create(env, stream, storage, swallowExceptions); } +static void free_pointer_skproc(const void* ptr, void*) { + free((void*)ptr); +} + sk_sp<SkData> CopyJavaInputStream(JNIEnv* env, jobject inputStream, jbyteArray storage) { std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, inputStream, storage)); if (!stream) { @@ -186,19 +190,31 @@ sk_sp<SkData> CopyJavaInputStream(JNIEnv* env, jobject inputStream, jbyteArray s size_t bufferSize = 4096; size_t streamLen = 0; size_t len; - char* data = (char*)sk_malloc_throw(bufferSize); + char* data = (char*)malloc(bufferSize); + LOG_ALWAYS_FATAL_IF(!data); while ((len = stream->read(data + streamLen, bufferSize - streamLen)) != 0) { streamLen += len; if (streamLen == bufferSize) { bufferSize *= 2; - data = (char*)sk_realloc_throw(data, bufferSize); + data = (char*)realloc(data, bufferSize); + LOG_ALWAYS_FATAL_IF(!data); } } - data = (char*)sk_realloc_throw(data, streamLen); - - return SkData::MakeFromMalloc(data, streamLen); + if (streamLen == 0) { + // realloc with size 0 is unspecified behavior in C++11 + free(data); + data = nullptr; + } else { + // Trim down the buffer to the actual size of the data. + LOG_FATAL_IF(streamLen > bufferSize); + data = (char*)realloc(data, streamLen); + LOG_ALWAYS_FATAL_IF(!data); + } + // Just in case sk_free differs from free, we ask Skia to use + // free to cleanup the buffer that SkData wraps. + return SkData::MakeWithProc(data, streamLen, free_pointer_skproc, nullptr); } /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp index 69774cc6e133..0c3af61fc089 100644 --- a/libs/hwui/jni/FontFamily.cpp +++ b/libs/hwui/jni/FontFamily.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "Minikin" - #include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/ScopedUtfChars.h> #include "FontUtils.h" @@ -89,7 +86,8 @@ static jlong FontFamily_create(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr) { } std::shared_ptr<minikin::FontFamily> family = minikin::FontFamily::create( builder->langId, builder->variant, std::move(builder->fonts), - true /* isCustomFallback */, false /* isDefaultFallback */); + true /* isCustomFallback */, false /* isDefaultFallback */, + minikin::VariationFamilyType::None); if (family->getCoverage().length() == 0) { return 0; } diff --git a/libs/hwui/jni/Gainmap.cpp b/libs/hwui/jni/Gainmap.cpp index cec0ee7ee247..0fffee744be0 100644 --- a/libs/hwui/jni/Gainmap.cpp +++ b/libs/hwui/jni/Gainmap.cpp @@ -208,8 +208,6 @@ static void Gainmap_writeToParcel(JNIEnv* env, jobject, jlong nativeObject, jobj p.writeFloat(info.fDisplayRatioHdr); // base image type p.writeInt32(static_cast<int32_t>(info.fBaseImageType)); - // type - p.writeInt32(static_cast<int32_t>(info.fType)); #else doThrowRE(env, "Cannot use parcels outside of Android!"); #endif @@ -232,7 +230,6 @@ static void Gainmap_readFromParcel(JNIEnv* env, jobject, jlong nativeObject, job info.fDisplayRatioSdr = p.readFloat(); info.fDisplayRatioHdr = p.readFloat(); info.fBaseImageType = static_cast<SkGainmapInfo::BaseImageType>(p.readInt32()); - info.fType = static_cast<SkGainmapInfo::Type>(p.readInt32()); fromJava(nativeObject)->info = info; #else diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 78b4f7b7654d..7cc48661619a 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -1,6 +1,3 @@ -#undef LOG_TAG -#define LOG_TAG "GraphicsJNI" - #include <assert.h> #include <unistd.h> diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h index 23ab5dd38b1a..b9fff36d372e 100644 --- a/libs/hwui/jni/GraphicsJNI.h +++ b/libs/hwui/jni/GraphicsJNI.h @@ -125,14 +125,6 @@ public: static jobject createBitmapRegionDecoder(JNIEnv* env, android::BitmapRegionDecoderWrapper* bitmap); - /** - * Given a bitmap we natively allocate a memory block to store the contents - * of that bitmap. The memory is then attached to the bitmap via an - * SkPixelRef, which ensures that upon deletion the appropriate caches - * are notified. - */ - static bool allocatePixels(JNIEnv* env, SkBitmap* bitmap); - /** Copy the colors in colors[] to the bitmap, convert to the correct format along the way. Whether to use premultiplied pixels is determined by dstBitmap's alphaType. diff --git a/libs/hwui/jni/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp index e32c9115483c..54369b9e4384 100644 --- a/libs/hwui/jni/GraphicsStatsService.cpp +++ b/libs/hwui/jni/GraphicsStatsService.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "GraphicsStatsService" - #include <JankTracker.h> #include <log/log.h> #include <nativehelper/ScopedPrimitiveArray.h> diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp index 048ce025ce27..cbd452031f69 100644 --- a/libs/hwui/jni/MaskFilter.cpp +++ b/libs/hwui/jni/MaskFilter.cpp @@ -1,6 +1,5 @@ #include "GraphicsJNI.h" #include "SkMaskFilter.h" -#include "SkBlurMask.h" #include "SkBlurMaskFilter.h" #include "SkBlurTypes.h" #include "SkTableMaskFilter.h" @@ -11,6 +10,13 @@ static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) { } } +// From https://skia.googlesource.com/skia/+/d74c99a3cd5eef5f16b2eb226e6b45fe523c8552/src/core/SkBlurMask.cpp#28 +static constexpr float kBLUR_SIGMA_SCALE = 0.57735f; + +static float convertRadiusToSigma(float radius) { + return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f; +} + class SkMaskFilterGlue { public: static void destructor(JNIEnv* env, jobject, jlong filterHandle) { @@ -19,7 +25,7 @@ public: } static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) { - SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); + SkScalar sigma = convertRadiusToSigma(radius); SkMaskFilter* filter = SkMaskFilter::MakeBlur((SkBlurStyle)blurStyle, sigma).release(); ThrowIAE_IfNull(env, filter); return reinterpret_cast<jlong>(filter); @@ -34,7 +40,7 @@ public: direction[i] = values[i]; } - SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); + SkScalar sigma = convertRadiusToSigma(radius); SkMaskFilter* filter = SkBlurMaskFilter::MakeEmboss(sigma, direction, ambient, specular).release(); ThrowIAE_IfNull(env, filter); diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp index d50a8a22b5cb..67ef143e6179 100644 --- a/libs/hwui/jni/NinePatch.cpp +++ b/libs/hwui/jni/NinePatch.cpp @@ -15,8 +15,6 @@ ** limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "9patch" #define LOG_NDEBUG 1 #include <androidfw/ResourceTypes.h> diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index 13357fa25e8c..14639456f13b 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -15,16 +15,29 @@ ** limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "Paint" - -#include <utils/Log.h> - -#include "GraphicsJNI.h" +#include <hwui/BlurDrawLooper.h> +#include <hwui/MinikinSkia.h> +#include <hwui/MinikinUtils.h> +#include <hwui/Paint.h> +#include <hwui/Typeface.h> +#include <minikin/GraphemeBreak.h> +#include <minikin/LocaleList.h> +#include <minikin/Measurement.h> +#include <minikin/MinikinPaint.h> +#include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/ScopedStringChars.h> #include <nativehelper/ScopedUtfChars.h> -#include <nativehelper/ScopedPrimitiveArray.h> +#include <unicode/utf16.h> +#include <utils/Log.h> + +#include <cassert> +#include <cstring> +#include <memory> +#include <vector> +#include "ColorFilter.h" +#include "GraphicsJNI.h" +#include "SkBlendMode.h" #include "SkColorFilter.h" #include "SkColorSpace.h" #include "SkFont.h" @@ -35,27 +48,21 @@ #include "SkPathEffect.h" #include "SkPathUtils.h" #include "SkShader.h" -#include "SkBlendMode.h" #include "unicode/uloc.h" #include "utils/Blur.h" -#include <hwui/BlurDrawLooper.h> -#include <hwui/MinikinSkia.h> -#include <hwui/MinikinUtils.h> -#include <hwui/Paint.h> -#include <hwui/Typeface.h> -#include <minikin/GraphemeBreak.h> -#include <minikin/LocaleList.h> -#include <minikin/Measurement.h> -#include <minikin/MinikinPaint.h> -#include <unicode/utf16.h> +namespace android { -#include <cassert> -#include <cstring> -#include <memory> -#include <vector> +namespace { -namespace android { +void copyMinikinRectToSkRect(const minikin::MinikinRect& minikinRect, SkRect* skRect) { + skRect->fLeft = minikinRect.mLeft; + skRect->fTop = minikinRect.mTop; + skRect->fRight = minikinRect.mRight; + skRect->fBottom = minikinRect.mBottom; +} + +} // namespace static void getPosTextPath(const SkFont& font, const uint16_t glyphs[], int count, const SkPoint pos[], SkPath* dst) { @@ -105,8 +112,8 @@ namespace PaintGlue { float measured = 0; std::unique_ptr<float[]> advancesArray(new float[count]); - MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, - 0, count, count, advancesArray.get()); + MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, 0, + count, count, advancesArray.get(), nullptr); for (int i = 0; i < count; i++) { // traverse in the given direction @@ -196,9 +203,9 @@ namespace PaintGlue { if (advances) { advancesArray.reset(new jfloat[count]); } - const float advance = MinikinUtils::measureText(paint, - static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count, contextCount, - advancesArray.get()); + const float advance = MinikinUtils::measureText( + paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count, + contextCount, advancesArray.get(), nullptr); if (advances) { env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get()); } @@ -236,7 +243,7 @@ namespace PaintGlue { minikin::Bidi bidiFlags = dir == 1 ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; std::unique_ptr<float[]> advancesArray(new float[count]); MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count, - advancesArray.get()); + advancesArray.get(), nullptr); size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text, start, count, offset, moveOpt); return static_cast<jint>(result); @@ -500,7 +507,7 @@ namespace PaintGlue { static jfloat doRunAdvance(JNIEnv* env, const Paint* paint, const Typeface* typeface, const jchar buf[], jint start, jint count, jint bufSize, jboolean isRtl, jint offset, jfloatArray advances, - jint advancesIndex) { + jint advancesIndex, SkRect* drawBounds) { if (advances) { size_t advancesLength = env->GetArrayLength(advances); if ((size_t)(count + advancesIndex) > advancesLength) { @@ -509,14 +516,23 @@ namespace PaintGlue { } } minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; + minikin::MinikinRect bounds; if (offset == start + count && advances == nullptr) { - return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, - bufSize, nullptr); + float result = + MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, + bufSize, nullptr, drawBounds ? &bounds : nullptr); + if (drawBounds) { + copyMinikinRectToSkRect(bounds, drawBounds); + } + return result; } std::unique_ptr<float[]> advancesArray(new float[count]); MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, - advancesArray.get()); + advancesArray.get(), drawBounds ? &bounds : nullptr); + if (drawBounds) { + copyMinikinRectToSkRect(bounds, drawBounds); + } float result = minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset); if (advances) { minikin::distributeAdvances(advancesArray.get(), buf, start, count); @@ -532,7 +548,7 @@ namespace PaintGlue { ScopedCharArrayRO textArray(env, text); jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart, start - contextStart, end - start, contextEnd - contextStart, - isRtl, offset - contextStart, nullptr, 0); + isRtl, offset - contextStart, nullptr, 0, nullptr); return result; } @@ -540,13 +556,19 @@ namespace PaintGlue { jcharArray text, jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl, jint offset, - jfloatArray advances, jint advancesIndex) { + jfloatArray advances, jint advancesIndex, + jobject drawBounds) { const Paint* paint = reinterpret_cast<Paint*>(paintHandle); const Typeface* typeface = paint->getAndroidTypeface(); ScopedCharArrayRO textArray(env, text); + SkRect skDrawBounds; jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart, start - contextStart, end - start, contextEnd - contextStart, - isRtl, offset - contextStart, advances, advancesIndex); + isRtl, offset - contextStart, advances, advancesIndex, + drawBounds ? &skDrawBounds : nullptr); + if (drawBounds != nullptr) { + GraphicsJNI::rect_to_jrectf(skDrawBounds, env, drawBounds); + } return result; } @@ -555,7 +577,7 @@ namespace PaintGlue { minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; std::unique_ptr<float[]> advancesArray(new float[count]); MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, - advancesArray.get()); + advancesArray.get(), nullptr); return minikin::getOffsetForAdvance(advancesArray.get(), buf, start, count, advance); } @@ -571,7 +593,7 @@ namespace PaintGlue { return result; } - static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) { + static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics* metrics, bool useLocale) { const int kElegantTop = 2500; const int kElegantBottom = -1000; const int kElegantAscent = 1900; @@ -584,7 +606,7 @@ namespace PaintGlue { minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle); float saveSkewX = font->getSkewX(); bool savefakeBold = font->isEmbolden(); - MinikinFontSkia::populateSkFont(font, baseFont.font->typeface().get(), baseFont.fakery); + MinikinFontSkia::populateSkFont(font, baseFont.typeface().get(), baseFont.fakery); SkScalar spacing = font->getMetrics(metrics); // The populateSkPaint call may have changed fake bold / text skew // because we want to measure with those effects applied, so now @@ -600,6 +622,17 @@ namespace PaintGlue { metrics->fLeading = size * kElegantLeading / 2048; spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading; } + + if (useLocale) { + minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface); + minikin::MinikinExtent extent = + typeface->fFontCollection->getReferenceExtentForLocale(minikinPaint); + metrics->fAscent = std::min(extent.ascent, metrics->fAscent); + metrics->fDescent = std::max(extent.descent, metrics->fDescent); + metrics->fTop = std::min(metrics->fAscent, metrics->fTop); + metrics->fBottom = std::max(metrics->fDescent, metrics->fBottom); + } + return spacing; } @@ -612,7 +645,7 @@ namespace PaintGlue { MinikinUtils::getFontExtent(paint, bidiFlags, typeface, buf, start, count, bufSize); SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, false /* useLocale */); metrics.fAscent = extent.ascent; metrics.fDescent = extent.descent; @@ -664,20 +697,21 @@ namespace PaintGlue { } } - static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) { + static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj, + jboolean useLocale) { SkFontMetrics metrics; - SkScalar spacing = getMetricsInternal(paintHandle, &metrics); + SkScalar spacing = getMetricsInternal(paintHandle, &metrics, useLocale); GraphicsJNI::set_metrics(env, metricsObj, metrics); return SkScalarToFloat(spacing); } - static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) { + static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj, + jboolean useLocale) { SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, useLocale); return GraphicsJNI::set_metrics_int(env, metricsObj, metrics); } - // ------------------ @CriticalNative --------------------------- static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { @@ -821,9 +855,11 @@ namespace PaintGlue { static jlong setColorFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong filterHandle) { Paint* obj = reinterpret_cast<Paint *>(objHandle); - SkColorFilter* filter = reinterpret_cast<SkColorFilter *>(filterHandle); - obj->setColorFilter(sk_ref_sp(filter)); - return reinterpret_cast<jlong>(obj->getColorFilter()); + auto colorFilter = uirenderer::ColorFilter::fromJava(filterHandle); + auto skColorFilter = + colorFilter != nullptr ? colorFilter->getInstance() : sk_sp<SkColorFilter>(); + obj->setColorFilter(skColorFilter); + return filterHandle; } static void setXfermode(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint xfermodeHandle) { @@ -978,19 +1014,19 @@ namespace PaintGlue { static jfloat ascent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, false /* useLocale */); return SkScalarToFloat(metrics.fAscent); } static jfloat descent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, false /* useLocale */); return SkScalarToFloat(metrics.fDescent); } static jfloat getUnderlinePosition(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, false /* useLocale */); SkScalar position; if (metrics.hasUnderlinePosition(&position)) { return SkScalarToFloat(position); @@ -1002,7 +1038,7 @@ namespace PaintGlue { static jfloat getUnderlineThickness(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { SkFontMetrics metrics; - getMetricsInternal(paintHandle, &metrics); + getMetricsInternal(paintHandle, &metrics, false /* useLocale */); SkScalar thickness; if (metrics.hasUnderlineThickness(&thickness)) { return SkScalarToFloat(thickness); @@ -1083,7 +1119,7 @@ static const JNINativeMethod methods[] = { (void*)PaintGlue::getCharArrayBounds}, {"nHasGlyph", "(JILjava/lang/String;)Z", (void*)PaintGlue::hasGlyph}, {"nGetRunAdvance", "(J[CIIIIZI)F", (void*)PaintGlue::getRunAdvance___CIIIIZI_F}, - {"nGetRunCharacterAdvance", "(J[CIIIIZI[FI)F", + {"nGetRunCharacterAdvance", "(J[CIIIIZI[FILandroid/graphics/RectF;)F", (void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F}, {"nGetOffsetForAdvance", "(J[CIIIIZF)I", (void*)PaintGlue::getOffsetForAdvance___CIIIIZF_I}, {"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V", @@ -1097,9 +1133,9 @@ static const JNINativeMethod methods[] = { {"nSetTextLocales", "(JLjava/lang/String;)I", (void*)PaintGlue::setTextLocales}, {"nSetFontFeatureSettings", "(JLjava/lang/String;)V", (void*)PaintGlue::setFontFeatureSettings}, - {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F", + {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;Z)F", (void*)PaintGlue::getFontMetrics}, - {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I", + {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;Z)I", (void*)PaintGlue::getFontMetricsInt}, // --------------- @CriticalNative ------------------ diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp index f3db1705e694..dcd3fa4932fc 100644 --- a/libs/hwui/jni/RenderEffect.cpp +++ b/libs/hwui/jni/RenderEffect.cpp @@ -14,13 +14,13 @@ * limitations under the License. */ #include "Bitmap.h" +#include "ColorFilter.h" #include "GraphicsJNI.h" #include "SkBlendMode.h" #include "SkImageFilter.h" #include "SkImageFilters.h" #include "graphics_jni_helpers.h" #include "utils/Blur.h" -#include <utils/Log.h> using namespace android::uirenderer; @@ -76,11 +76,13 @@ static jlong createColorFilterEffect( jlong colorFilterHandle, jlong inputFilterHandle ) { - auto* colorFilter = reinterpret_cast<const SkColorFilter*>(colorFilterHandle); + auto colorFilter = android::uirenderer::ColorFilter::fromJava(colorFilterHandle); + auto skColorFilter = + colorFilter != nullptr ? colorFilter->getInstance() : sk_sp<SkColorFilter>(); auto* inputFilter = reinterpret_cast<const SkImageFilter*>(inputFilterHandle); - sk_sp<SkImageFilter> colorFilterImageFilter = SkImageFilters::ColorFilter( - sk_ref_sp(colorFilter), sk_ref_sp(inputFilter), nullptr); - return reinterpret_cast<jlong>(colorFilterImageFilter.release()); + sk_sp<SkImageFilter> colorFilterImageFilter = + SkImageFilters::ColorFilter(skColorFilter, sk_ref_sp(inputFilter), nullptr); + return reinterpret_cast<jlong>(colorFilterImageFilter.release()); } static jlong createBlendModeEffect( diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index 7eb79be6f55b..a952be020855 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -1,6 +1,3 @@ -#undef LOG_TAG -#define LOG_TAG "ShaderJNI" - #include <vector> #include "Gainmap.h" @@ -68,21 +65,41 @@ static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref)); } -static jlong createBitmapShaderHelper(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle, - jint tileModeX, jint tileModeY, bool isDirectSampled, - const SkSamplingOptions& sampling) { +/////////////////////////////////////////////////////////////////////////////////////////////// + +static SkGainmapInfo sNoOpGainmap = { + .fGainmapRatioMin = {1.f, 1.f, 1.f, 1.0}, + .fGainmapRatioMax = {1.f, 1.f, 1.f, 1.0}, + .fGainmapGamma = {1.f, 1.f, 1.f, 1.f}, + .fEpsilonSdr = {0.f, 0.f, 0.f, 1.0}, + .fEpsilonHdr = {0.f, 0.f, 0.f, 1.0}, + .fDisplayRatioSdr = 1.f, + .fDisplayRatioHdr = 1.f, +}; + +static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle, + jint tileModeX, jint tileModeY, jint maxAniso, bool filter, + bool isDirectSampled, jlong overrideGainmapPtr) { + SkSamplingOptions sampling = maxAniso > 0 ? SkSamplingOptions::Aniso(static_cast<int>(maxAniso)) + : SkSamplingOptions(filter ? SkFilterMode::kLinear + : SkFilterMode::kNearest, + SkMipmapMode::kNone); const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + const Gainmap* gainmap = reinterpret_cast<Gainmap*>(overrideGainmapPtr); sk_sp<SkImage> image; if (bitmapHandle) { // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise, // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility. auto& bitmap = android::bitmap::toBitmap(bitmapHandle); image = bitmap.makeImage(); + if (!gainmap && bitmap.hasGainmap()) { + gainmap = bitmap.gainmap().get(); + } - if (!isDirectSampled && bitmap.hasGainmap()) { - sk_sp<SkShader> gainmapShader = MakeGainmapShader( - image, bitmap.gainmap()->bitmap->makeImage(), bitmap.gainmap()->info, - (SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling); + if (!isDirectSampled && gainmap && gainmap->info != sNoOpGainmap) { + sk_sp<SkShader> gainmapShader = + MakeGainmapShader(image, gainmap->bitmap->makeImage(), gainmap->info, + (SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling); if (gainmapShader) { if (matrix) { gainmapShader = gainmapShader->makeWithLocalMatrix(*matrix); @@ -114,26 +131,6 @@ static jlong createBitmapShaderHelper(JNIEnv* env, jobject o, jlong matrixPtr, j /////////////////////////////////////////////////////////////////////////////////////////////// -static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle, - jint tileModeX, jint tileModeY, bool filter, - bool isDirectSampled) { - SkSamplingOptions sampling(filter ? SkFilterMode::kLinear : SkFilterMode::kNearest, - SkMipmapMode::kNone); - return createBitmapShaderHelper(env, o, matrixPtr, bitmapHandle, tileModeX, tileModeY, - isDirectSampled, sampling); -} - -static jlong BitmapShader_constructorWithMaxAniso(JNIEnv* env, jobject o, jlong matrixPtr, - jlong bitmapHandle, jint tileModeX, - jint tileModeY, jint maxAniso, - bool isDirectSampled) { - auto sampling = SkSamplingOptions::Aniso(static_cast<int>(maxAniso)); - return createBitmapShaderHelper(env, o, matrixPtr, bitmapHandle, tileModeX, tileModeY, - isDirectSampled, sampling); -} - -/////////////////////////////////////////////////////////////////////////////////////////////// - static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) { const size_t count = env->GetArrayLength(colorArray); const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr); @@ -422,8 +419,7 @@ static const JNINativeMethod gShaderMethods[] = { }; static const JNINativeMethod gBitmapShaderMethods[] = { - {"nativeCreate", "(JJIIZZ)J", (void*)BitmapShader_constructor}, - {"nativeCreateWithMaxAniso", "(JJIIIZ)J", (void*)BitmapShader_constructorWithMaxAniso}, + {"nativeCreate", "(JJIIIZZJ)J", (void*)BitmapShader_constructor}, }; diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp index 69418b09fee6..4dbfa88d6301 100644 --- a/libs/hwui/jni/YuvToJpegEncoder.cpp +++ b/libs/hwui/jni/YuvToJpegEncoder.cpp @@ -1,6 +1,3 @@ -#undef LOG_TAG -#define LOG_TAG "YuvToJpegEncoder" - #include "CreateJavaOutputStreamAdaptor.h" #include "SkStream.h" #include "YuvToJpegEncoder.h" diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp index f060bb32031a..426644ee6a4e 100644 --- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp +++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp @@ -84,7 +84,7 @@ static void android_view_DisplayListCanvas_resetDisplayListCanvas(CRITICAL_JNI_P canvas->resetRecording(width, height, renderNode); } -static jint android_view_DisplayListCanvas_getMaxTextureSize(CRITICAL_JNI_PARAMS) { +static jint android_view_DisplayListCanvas_getMaxTextureSize(JNIEnv*, jobject) { #ifdef __ANDROID__ // Layoutlib does not support RenderProxy (RenderThread) return android::uirenderer::renderthread::RenderProxy::maxTextureSize(); #else @@ -175,14 +175,14 @@ static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAM const char* const kClassPathName = "android/graphics/RecordingCanvas"; static JNINativeMethod gMethods[] = { + {"nGetMaximumTextureWidth", "()I", (void*)android_view_DisplayListCanvas_getMaxTextureSize}, + {"nGetMaximumTextureHeight", "()I", + (void*)android_view_DisplayListCanvas_getMaxTextureSize}, // ------------ @CriticalNative -------------- {"nCreateDisplayListCanvas", "(JII)J", (void*)android_view_DisplayListCanvas_createDisplayListCanvas}, {"nResetDisplayListCanvas", "(JJII)V", (void*)android_view_DisplayListCanvas_resetDisplayListCanvas}, - {"nGetMaximumTextureWidth", "()I", (void*)android_view_DisplayListCanvas_getMaxTextureSize}, - {"nGetMaximumTextureHeight", "()I", - (void*)android_view_DisplayListCanvas_getMaxTextureSize}, {"nEnableZ", "(JZ)V", (void*)android_view_DisplayListCanvas_enableZ}, {"nFinishRecording", "(JJ)V", (void*)android_view_DisplayListCanvas_finishRecording}, {"nDrawRenderNode", "(JJ)V", (void*)android_view_DisplayListCanvas_drawRenderNode}, diff --git a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp index 706f18c3be80..e3cdee6e7034 100644 --- a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "HardwareBufferRenderer" #define ATRACE_TAG ATRACE_TAG_VIEW #include <GraphicsJNI.h> diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index d04de37f6961..422ffeaecfd0 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "ThreadedRenderer" #define ATRACE_TAG ATRACE_TAG_VIEW #include <FrameInfo.h> @@ -27,7 +25,7 @@ #include <SkColorSpace.h> #include <SkData.h> #include <SkImage.h> -#include <SkImagePriv.h> +#include <SkImageAndroid.h> #include <SkPicture.h> #include <SkPixmap.h> #include <SkSerialProcs.h> @@ -35,6 +33,7 @@ #include <SkTypeface.h> #include <dlfcn.h> #include <gui/TraceUtils.h> +#include <include/encode/SkPngEncoder.h> #include <inttypes.h> #include <media/NdkImage.h> #include <media/NdkImageReader.h> @@ -54,6 +53,7 @@ #include <algorithm> #include <atomic> +#include <log/log.h> #include <vector> #include "JvmErrorReporter.h" @@ -477,7 +477,7 @@ public: // actually cross thread boundaries here, make a copy so it's immutable proper if (bitmap && !bitmap->isImmutable()) { ATRACE_NAME("Copying mutable bitmap"); - return SkImage::MakeFromBitmap(*bitmap); + return SkImages::RasterFromBitmap(*bitmap); } if (img->isTextureBacked()) { ATRACE_NAME("Readback of texture image"); @@ -497,7 +497,7 @@ public: return sk_ref_sp(img); } bm.setImmutable(); - return SkMakeImageFromRasterBitmap(bm, kNever_SkCopyPixelsMode); + return SkImages::PinnableRasterFromBitmap(bm); } return sk_ref_sp(img); } @@ -524,7 +524,16 @@ public: if (iter != context->mTextureMap.end()) { img = iter->second.get(); } - return img->encodeToData(); + if (!img) { + return nullptr; + } + // The following encode (specifically the pixel readback) will fail on a + // texture-backed image. They should already be raster images, but on + // the off-chance they aren't, we will just serialize it as nothing. + if (img->isTextureBacked()) { + return SkData::MakeEmpty(); + } + return SkPngEncoder::Encode(nullptr, img, {}); } void serialize(SkWStream* stream) const override { diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp index 8c7b9a4b5e94..2a218a25913d 100644 --- a/libs/hwui/jni/android_graphics_RenderNode.cpp +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -243,6 +243,13 @@ static jboolean android_view_RenderNode_setRenderEffect(CRITICAL_JNI_PARAMS_COMM return SET_AND_DIRTY(mutateLayerProperties().setImageFilter, imageFilter, RenderNode::GENERIC); } +static jboolean android_view_RenderNode_setBackdropRenderEffect( + CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong renderEffectPtr) { + SkImageFilter* imageFilter = reinterpret_cast<SkImageFilter*>(renderEffectPtr); + return SET_AND_DIRTY(mutateLayerProperties().setBackdropImageFilter, imageFilter, + RenderNode::GENERIC); +} + static jboolean android_view_RenderNode_setHasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, bool hasOverlappingRendering) { return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering, @@ -792,6 +799,8 @@ static const JNINativeMethod gMethods[] = { {"nSetAlpha", "(JF)Z", (void*)android_view_RenderNode_setAlpha}, {"nSetRenderEffect", "(JJ)Z", (void*)android_view_RenderNode_setRenderEffect}, + {"nSetBackdropRenderEffect", "(JJ)Z", + (void*)android_view_RenderNode_setBackdropRenderEffect}, {"nSetHasOverlappingRendering", "(JZ)Z", (void*)android_view_RenderNode_setHasOverlappingRendering}, {"nSetUsageHint", "(JI)V", (void*)android_view_RenderNode_setUsageHint}, diff --git a/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp index 764eff9a04be..b86c74fe0e47 100644 --- a/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp +++ b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" - #include <Interpolator.h> #include <cutils/log.h> diff --git a/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp index c6d26f853c1d..40be9243affb 100644 --- a/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp +++ b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" - #include <Animator.h> #include <Interpolator.h> #include <RenderProperties.h> diff --git a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp index 9cffceb308c8..ade48f26937f 100644 --- a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp +++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp @@ -14,13 +14,13 @@ * limitations under the License. */ -#include "GraphicsJNI.h" +#include <hwui/Paint.h> +#include "ColorFilter.h" +#include "GraphicsJNI.h" #include "PathParser.h" #include "VectorDrawable.h" -#include <hwui/Paint.h> - namespace android { using namespace uirenderer; using namespace uirenderer::VectorDrawable; @@ -108,8 +108,9 @@ static jint draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr, Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); SkRect rect; GraphicsJNI::jrect_to_rect(env, jrect, &rect); - SkColorFilter* colorFilter = reinterpret_cast<SkColorFilter*>(colorFilterPtr); - return tree->draw(canvas, colorFilter, rect, needsMirroring, canReuseCache); + auto colorFilter = ColorFilter::fromJava(colorFilterPtr); + auto skColorFilter = colorFilter != nullptr ? colorFilter->getInstance() : nullptr; + return tree->draw(canvas, skColorFilter.get(), rect, needsMirroring, canReuseCache); } /** diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index 1af60b2f5fae..2ec94c954fe9 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "Minikin" - #include "Font.h" #include "SkData.h" #include "SkFont.h" @@ -127,7 +124,7 @@ static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jo static jlong Font_Builder_clone(JNIEnv* env, jobject clazz, jlong fontPtr, jlong builderPtr, jint weight, jboolean italic, jint ttcIndex) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->typeface().get()); + MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->baseTypeface().get()); std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr)); // Reconstruct SkTypeface with different arguments from existing SkTypeface. @@ -159,7 +156,7 @@ static jlong Font_Builder_clone(JNIEnv* env, jobject clazz, jlong fontPtr, jlong static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint glyphId, jlong paintHandle, jobject rect) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->typeface().get()); + MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->baseTypeface().get()); Paint* paint = reinterpret_cast<Paint*>(paintHandle); SkFont* skFont = &paint->getSkFont(); @@ -179,7 +176,7 @@ static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint g static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong paintHandle, jobject metricsObj) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->typeface().get()); + MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font->baseTypeface().get()); Paint* paint = reinterpret_cast<Paint*>(paintHandle); SkFont* skFont = &paint->getSkFont(); @@ -209,7 +206,7 @@ static jlong Font_cloneFont(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { // Fast Native static jobject Font_newByteBuffer(JNIEnv* env, jobject, jlong fontPtr) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); - const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->baseTypeface(); return env->NewDirectByteBuffer(const_cast<void*>(minikinFont->GetFontData()), minikinFont->GetFontSize()); } @@ -217,7 +214,7 @@ static jobject Font_newByteBuffer(JNIEnv* env, jobject, jlong fontPtr) { // Critical Native static jlong Font_getBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); - return reinterpret_cast<jlong>(font->font->typeface()->GetFontData()); + return reinterpret_cast<jlong>(font->font->baseTypeface()->GetFontData()); } // Critical Native @@ -236,7 +233,7 @@ static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) { } return env->NewStringUTF(path.c_str()); } else { - const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->baseTypeface(); const std::string& path = minikinFont->GetFontPath(); if (path.empty()) { return nullptr; @@ -275,7 +272,7 @@ static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { reader.skipString(); // fontPath return reader.read<int>(); } else { - const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->baseTypeface(); return minikinFont->GetFontIndex(); } } @@ -289,7 +286,7 @@ static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { reader.skip<int>(); // fontIndex return reader.readArray<minikin::FontVariation>().second; } else { - const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->baseTypeface(); return minikinFont->GetAxes().size(); } } @@ -304,7 +301,7 @@ static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint inde reader.skip<int>(); // fontIndex var = reader.readArray<minikin::FontVariation>().first[index]; } else { - const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->baseTypeface(); var = minikinFont->GetAxes().at(index); } uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value); @@ -314,7 +311,7 @@ static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint inde // Critical Native static jint Font_getSourceId(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); - return font->font->typeface()->GetSourceId(); + return font->font->baseTypeface()->GetSourceId(); } static jlongArray Font_getAvailableFontSet(JNIEnv* env, jobject) { diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp index ee158ee4e316..462c8c8f2fb0 100644 --- a/libs/hwui/jni/fonts/FontFamily.cpp +++ b/libs/hwui/jni/fonts/FontFamily.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "Minikin" - #include "graphics_jni_helpers.h" #include <nativehelper/ScopedUtfChars.h> @@ -60,7 +57,7 @@ static void FontFamily_Builder_addFont(CRITICAL_JNI_PARAMS_COMMA jlong builderPt // Regular JNI static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jstring langTags, jint variant, jboolean isCustomFallback, - jboolean isDefaultFallback) { + jboolean isDefaultFallback, jint variationFamilyType) { std::unique_ptr<NativeFamilyBuilder> builder(toBuilder(builderPtr)); uint32_t localeId; if (langTags == nullptr) { @@ -71,7 +68,8 @@ static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderP } std::shared_ptr<minikin::FontFamily> family = minikin::FontFamily::create( localeId, static_cast<minikin::FamilyVariant>(variant), std::move(builder->fonts), - isCustomFallback, isDefaultFallback); + isCustomFallback, isDefaultFallback, + static_cast<minikin::VariationFamilyType>(variationFamilyType)); if (family->getCoverage().length() == 0) { // No coverage means minikin rejected given font for some reasons. jniThrowException(env, "java/lang/IllegalArgumentException", @@ -121,7 +119,7 @@ static jlong FontFamily_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr, jint static const JNINativeMethod gFontFamilyBuilderMethods[] = { {"nInitBuilder", "()J", (void*)FontFamily_Builder_initBuilder}, {"nAddFont", "(JJ)V", (void*)FontFamily_Builder_addFont}, - {"nBuild", "(JLjava/lang/String;IZZ)J", (void*)FontFamily_Builder_build}, + {"nBuild", "(JLjava/lang/String;IZZI)J", (void*)FontFamily_Builder_build}, {"nGetReleaseNativeFamily", "()J", (void*)FontFamily_Builder_GetReleaseFunc}, }; diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp index 427bafa1bd83..3b18f5f54187 100644 --- a/libs/hwui/jni/pdf/PdfEditor.cpp +++ b/libs/hwui/jni/pdf/PdfEditor.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "PdfEditor" - #include <sys/types.h> #include <unistd.h> diff --git a/libs/hwui/jni/pdf/PdfUtils.cpp b/libs/hwui/jni/pdf/PdfUtils.cpp index 06d202828b85..6887fdacd68f 100644 --- a/libs/hwui/jni/pdf/PdfUtils.cpp +++ b/libs/hwui/jni/pdf/PdfUtils.cpp @@ -16,14 +16,11 @@ #include "PdfUtils.h" -#include "jni.h" #include <nativehelper/JNIHelp.h> +#include <utils/Log.h> #include "fpdfview.h" - -#undef LOG_TAG -#define LOG_TAG "PdfUtils" -#include <utils/Log.h> +#include "jni.h" namespace android { diff --git a/libs/hwui/jni/text/GraphemeBreak.cpp b/libs/hwui/jni/text/GraphemeBreak.cpp index 55f03bd9f7b1..322af7e9f3ee 100644 --- a/libs/hwui/jni/text/GraphemeBreak.cpp +++ b/libs/hwui/jni/text/GraphemeBreak.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "GraphemeBreaker" - #include <minikin/GraphemeBreak.h> #include <nativehelper/ScopedPrimitiveArray.h> diff --git a/libs/hwui/jni/text/LineBreaker.cpp b/libs/hwui/jni/text/LineBreaker.cpp index 69865171a09d..c512256ed9b9 100644 --- a/libs/hwui/jni/text/LineBreaker.cpp +++ b/libs/hwui/jni/text/LineBreaker.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "LineBreaker" - #include "utils/misc.h" #include "utils/Log.h" #include "graphics_jni_helpers.h" @@ -54,13 +51,12 @@ static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) { // set text and set a number of parameters for creating a layout (width, tabstops, strategy, // hyphenFrequency) -static jlong nInit(JNIEnv* env, jclass /* unused */, - jint breakStrategy, jint hyphenationFrequency, jboolean isJustified, jintArray indents) { +static jlong nInit(JNIEnv* env, jclass /* unused */, jint breakStrategy, jint hyphenationFrequency, + jboolean isJustified, jintArray indents, jboolean useBoundsForWidth) { return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative( static_cast<minikin::BreakStrategy>(breakStrategy), - static_cast<minikin::HyphenationFrequency>(hyphenationFrequency), - isJustified, - jintArrayToFloatVector(env, indents))); + static_cast<minikin::HyphenationFrequency>(hyphenationFrequency), isJustified, + jintArrayToFloatVector(env, indents), useBoundsForWidth)); } static void nFinish(jlong nativePtr) { @@ -131,39 +127,44 @@ static jlong nGetReleaseResultFunc(CRITICAL_JNI_PARAMS) { } static const JNINativeMethod gMethods[] = { - // Fast Natives - {"nInit", "(" - "I" // breakStrategy - "I" // hyphenationFrequency - "Z" // isJustified - "[I" // indents - ")J", (void*) nInit}, - - // Critical Natives - {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, - - // Regular JNI - {"nComputeLineBreaks", "(" - "J" // nativePtr - "[C" // text - "J" // MeasuredParagraph ptr. - "I" // length - "F" // firstWidth - "I" // firstWidthLineCount - "F" // restWidth - "[F" // variableTabStops - "F" // defaultTabStop - "I" // indentsOffset - ")J", (void*) nComputeLineBreaks}, - - // Result accessors, CriticalNatives - {"nGetLineCount", "(J)I", (void*)nGetLineCount}, - {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset}, - {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth}, - {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent}, - {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent}, - {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag}, - {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc}, + // Fast Natives + {"nInit", + "(" + "I" // breakStrategy + "I" // hyphenationFrequency + "Z" // isJustified + "[I" // indents + "Z" // useBoundsForWidth + ")J", + (void*)nInit}, + + // Critical Natives + {"nGetReleaseFunc", "()J", (void*)nGetReleaseFunc}, + + // Regular JNI + {"nComputeLineBreaks", + "(" + "J" // nativePtr + "[C" // text + "J" // MeasuredParagraph ptr. + "I" // length + "F" // firstWidth + "I" // firstWidthLineCount + "F" // restWidth + "[F" // variableTabStops + "F" // defaultTabStop + "I" // indentsOffset + ")J", + (void*)nComputeLineBreaks}, + + // Result accessors, CriticalNatives + {"nGetLineCount", "(J)I", (void*)nGetLineCount}, + {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset}, + {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth}, + {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent}, + {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent}, + {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag}, + {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc}, }; int register_android_graphics_text_LineBreaker(JNIEnv* env) { diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp index c13c800651ef..746745afbf09 100644 --- a/libs/hwui/jni/text/MeasuredText.cpp +++ b/libs/hwui/jni/text/MeasuredText.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "MeasuredText" - #include "GraphicsJNI.h" #include "utils/misc.h" #include "utils/Log.h" @@ -65,13 +62,14 @@ static jlong nInitBuilder(CRITICAL_JNI_PARAMS) { // Regular JNI static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr, - jlong paintPtr, jint lbStyle, jint lbWordStyle, jint start, jint end, - jboolean isRtl) { + jlong paintPtr, jint lbStyle, jint lbWordStyle, jboolean hyphenation, + jint start, jint end, jboolean isRtl) { Paint* paint = toPaint(paintPtr); const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface()); minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface); toBuilder(builderPtr) - ->addStyleRun(start, end, std::move(minikinPaint), lbStyle, lbWordStyle, isRtl); + ->addStyleRun(start, end, std::move(minikinPaint), lbStyle, lbWordStyle, hyphenation, + isRtl); } // Regular JNI @@ -84,13 +82,14 @@ static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong // Regular JNI static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr, jlong hintPtr, jcharArray javaText, jboolean computeHyphenation, - jboolean computeLayout, jboolean fastHyphenationMode) { + jboolean computeLayout, jboolean computeBounds, + jboolean fastHyphenationMode) { ScopedCharArrayRO text(env, javaText); const minikin::U16StringPiece textBuffer(text.get(), text.size()); // Pass the ownership to Java. return toJLong(toBuilder(builderPtr) - ->build(textBuffer, computeHyphenation, computeLayout, + ->build(textBuffer, computeHyphenation, computeLayout, computeBounds, fastHyphenationMode, toMeasuredParagraph(hintPtr)) .release()); } @@ -161,9 +160,9 @@ static jint nGetMemoryUsage(CRITICAL_JNI_PARAMS_COMMA jlong ptr) { static const JNINativeMethod gMTBuilderMethods[] = { // MeasuredParagraphBuilder native functions. {"nInitBuilder", "()J", (void*)nInitBuilder}, - {"nAddStyleRun", "(JJIIIIZ)V", (void*)nAddStyleRun}, + {"nAddStyleRun", "(JJIIZIIZ)V", (void*)nAddStyleRun}, {"nAddReplacementRun", "(JJIIF)V", (void*)nAddReplacementRun}, - {"nBuildMeasuredText", "(JJ[CZZZ)J", (void*)nBuildMeasuredText}, + {"nBuildMeasuredText", "(JJ[CZZZZ)J", (void*)nBuildMeasuredText}, {"nFreeBuilder", "(J)V", (void*)nFreeBuilder}, }; diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp index 8e4dd53069f4..6c05346d26da 100644 --- a/libs/hwui/jni/text/TextShaper.cpp +++ b/libs/hwui/jni/text/TextShaper.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#undef LOG_TAG -#define LOG_TAG "TextShaper" - #include "graphics_jni_helpers.h" #include <nativehelper/ScopedStringChars.h> #include <nativehelper/ScopedPrimitiveArray.h> @@ -62,7 +59,7 @@ static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int cou const minikin::Font* font = layout.getFont(i); if (seenFonts.find(font) != seenFonts.end()) continue; minikin::MinikinExtent extent = {}; - font->typeface()->GetFontExtent(&extent, minikinPaint, layout.getFakery(i)); + layout.typeface(i)->GetFontExtent(&extent, minikinPaint, layout.getFakery(i)); overallAscent = std::min(overallAscent, extent.ascent); overallDescent = std::max(overallDescent, extent.descent); } @@ -148,6 +145,30 @@ static jfloat TextShaper_Result_getY(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i } // CriticalNative +static jboolean TextShaper_Result_getFakeBold(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr); + return layout->layout.getFakery(i).isFakeBold(); +} + +// CriticalNative +static jboolean TextShaper_Result_getFakeItalic(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr); + return layout->layout.getFakery(i).isFakeItalic(); +} + +// CriticalNative +static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr); + return layout->layout.getFakery(i).wghtAdjustment(); +} + +// CriticalNative +static jfloat TextShaper_Result_getItalicOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr); + return layout->layout.getFakery(i).italAdjustment(); +} + +// CriticalNative static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr); std::shared_ptr<minikin::Font> fontRef = layout->layout.getFontRef(i); @@ -185,15 +206,19 @@ static const JNINativeMethod gMethods[] = { }; static const JNINativeMethod gResultMethods[] = { - { "nGetGlyphCount", "(J)I", (void*)TextShaper_Result_getGlyphCount }, - { "nGetTotalAdvance", "(J)F", (void*)TextShaper_Result_getTotalAdvance }, - { "nGetAscent", "(J)F", (void*)TextShaper_Result_getAscent }, - { "nGetDescent", "(J)F", (void*)TextShaper_Result_getDescent }, - { "nGetGlyphId", "(JI)I", (void*)TextShaper_Result_getGlyphId }, - { "nGetX", "(JI)F", (void*)TextShaper_Result_getX }, - { "nGetY", "(JI)F", (void*)TextShaper_Result_getY }, - { "nGetFont", "(JI)J", (void*)TextShaper_Result_getFont }, - { "nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc }, + {"nGetGlyphCount", "(J)I", (void*)TextShaper_Result_getGlyphCount}, + {"nGetTotalAdvance", "(J)F", (void*)TextShaper_Result_getTotalAdvance}, + {"nGetAscent", "(J)F", (void*)TextShaper_Result_getAscent}, + {"nGetDescent", "(J)F", (void*)TextShaper_Result_getDescent}, + {"nGetGlyphId", "(JI)I", (void*)TextShaper_Result_getGlyphId}, + {"nGetX", "(JI)F", (void*)TextShaper_Result_getX}, + {"nGetY", "(JI)F", (void*)TextShaper_Result_getY}, + {"nGetFont", "(JI)J", (void*)TextShaper_Result_getFont}, + {"nGetFakeBold", "(JI)Z", (void*)TextShaper_Result_getFakeBold}, + {"nGetFakeItalic", "(JI)Z", (void*)TextShaper_Result_getFakeItalic}, + {"nGetWeightOverride", "(JI)F", (void*)TextShaper_Result_getWeightOverride}, + {"nGetItalicOverride", "(JI)F", (void*)TextShaper_Result_getItalicOverride}, + {"nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc}, }; int register_android_graphics_text_TextShaper(JNIEnv* env) { diff --git a/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp b/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp new file mode 100644 index 000000000000..e81cbfb508ae --- /dev/null +++ b/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BackdropFilterDrawable.h" + +#include <SkImage.h> +#include <SkSurface.h> + +#include "RenderNode.h" +#include "RenderNodeDrawable.h" +#ifdef __ANDROID__ +#include "include/gpu/ganesh/SkImageGanesh.h" +#endif + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +BackdropFilterDrawable::~BackdropFilterDrawable() {} + +bool BackdropFilterDrawable::prepareToDraw(SkCanvas* canvas, const RenderProperties& properties, + int backdropImageWidth, int backdropImageHeight) { + // the drawing bounds for blurred content. + mDstBounds.setWH(properties.getWidth(), properties.getHeight()); + + float alphaMultiplier = 1.0f; + RenderNodeDrawable::setViewProperties(properties, canvas, &alphaMultiplier, true); + + // get proper subset for previous content. + canvas->getTotalMatrix().mapRect(&mImageSubset, mDstBounds); + SkRect imageSubset(mImageSubset); + // ensure the subset is inside bounds of previous content. + if (!mImageSubset.intersect(SkRect::MakeWH(backdropImageWidth, backdropImageHeight))) { + return false; + } + + // correct the drawing bounds if subset was changed. + if (mImageSubset != imageSubset) { + SkMatrix inverse; + if (canvas->getTotalMatrix().invert(&inverse)) { + inverse.mapRect(&mDstBounds, mImageSubset); + } + } + + // follow the alpha from the target RenderNode. + mPaint.setAlpha(properties.layerProperties().alpha() * alphaMultiplier); + return true; +} + +void BackdropFilterDrawable::onDraw(SkCanvas* canvas) { + const RenderProperties& properties = mTargetRenderNode->properties(); + auto* backdropFilter = properties.layerProperties().getBackdropImageFilter(); + auto* surface = canvas->getSurface(); + if (!backdropFilter || !surface) { + return; + } + + auto backdropImage = surface->makeImageSnapshot(); + // sync necessary properties from target RenderNode. + if (!prepareToDraw(canvas, properties, backdropImage->width(), backdropImage->height())) { + return; + } + + auto imageSubset = mImageSubset.roundOut(); +#ifdef __ANDROID__ + if (canvas->recordingContext()) { + backdropImage = + SkImages::MakeWithFilter(canvas->recordingContext(), backdropImage, backdropFilter, + imageSubset, imageSubset, &mOutSubset, &mOutOffset); + } else +#endif + { + backdropImage = SkImages::MakeWithFilter(backdropImage, backdropFilter, imageSubset, + imageSubset, &mOutSubset, &mOutOffset); + } + canvas->drawImageRect(backdropImage, SkRect::Make(mOutSubset), mDstBounds, + SkSamplingOptions(SkFilterMode::kLinear), &mPaint, + SkCanvas::kStrict_SrcRectConstraint); +} + +} // namespace skiapipeline +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/pipeline/skia/BackdropFilterDrawable.h b/libs/hwui/pipeline/skia/BackdropFilterDrawable.h new file mode 100644 index 000000000000..9e35837675ae --- /dev/null +++ b/libs/hwui/pipeline/skia/BackdropFilterDrawable.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <SkCanvas.h> +#include <SkDrawable.h> +#include <SkPaint.h> + +namespace android { +namespace uirenderer { + +class RenderNode; +class RenderProperties; + +namespace skiapipeline { + +/** + * This drawable captures it's backdrop content and render it with a + * image filter. + */ +class BackdropFilterDrawable : public SkDrawable { +public: + BackdropFilterDrawable(RenderNode* renderNode, SkCanvas* canvas) + : mTargetRenderNode(renderNode), mBounds(canvas->getLocalClipBounds()) {} + + ~BackdropFilterDrawable(); + +private: + RenderNode* mTargetRenderNode; + SkPaint mPaint; + + SkRect mDstBounds; + SkRect mImageSubset; + SkIRect mOutSubset; + SkIPoint mOutOffset; + + /** + * Check all necessary properties before actual drawing. + * Return true if ready to draw. + */ + bool prepareToDraw(SkCanvas* canvas, const RenderProperties& properties, int backdropImageWidth, + int backdropImageHeight); + +protected: + void onDraw(SkCanvas* canvas) override; + + virtual SkRect onGetBounds() override { return mBounds; } + const SkRect mBounds; +}; + +} // namespace skiapipeline +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index 8d5967bbd461..5d3fb30769ed 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -21,10 +21,15 @@ #include "GrBackendSurface.h" #include "RenderNode.h" #include "SkAndroidFrameworkUtils.h" +#include "SkCanvas.h" +#include "SkCanvasAndroid.h" #include "SkClipStack.h" #include "SkRect.h" #include "SkM44.h" +#include <include/gpu/ganesh/SkSurfaceGanesh.h> #include "include/gpu/GpuTypes.h" // from Skia +#include <include/gpu/gl/GrGLTypes.h> +#include <include/gpu/ganesh/gl/GrGLBackendSurface.h> #include "utils/GLUtils.h" #include <effects/GainmapRenderer.h> #include "renderthread/CanvasContext.h" @@ -34,7 +39,7 @@ namespace uirenderer { namespace skiapipeline { static void setScissor(int viewportHeight, const SkIRect& clip) { - SkASSERT(!clip.isEmpty()); + LOG_FATAL_IF(clip.isEmpty(), "empty scissor clip"); // transform to Y-flipped GL space, and prevent negatives GLint y = viewportHeight - clip.fBottom; GLint height = (viewportHeight - clip.fTop) - y; @@ -42,9 +47,9 @@ static void setScissor(int viewportHeight, const SkIRect& clip) { } static void GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) { - GrBackendRenderTarget renderTarget = canvas->topLayerBackendRenderTarget(); + GrBackendRenderTarget renderTarget = skgpu::ganesh::TopLayerBackendRenderTarget(canvas); GrGLFramebufferInfo fboInfo; - LOG_ALWAYS_FATAL_IF(!renderTarget.getGLFramebufferInfo(&fboInfo), + LOG_ALWAYS_FATAL_IF(!GrBackendRenderTargets::GetGLFramebufferInfo(renderTarget, &fboInfo), "getGLFrameBufferInfo failed"); *outFboID = fboInfo.fFBOID; @@ -76,13 +81,13 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { } // flush will create a GrRenderTarget if not already present. - canvas->flush(); + directContext->flushAndSubmit(); GLuint fboID = 0; SkISize fboSize; GetFboDetails(canvas, &fboID, &fboSize); - SkIRect surfaceBounds = canvas->topLayerBounds(); + SkIRect surfaceBounds = skgpu::ganesh::TopLayerBounds(canvas); SkIRect clipBounds = canvas->getDeviceClipBounds(); SkM44 mat4(canvas->getLocalToDevice()); SkRegion clipRegion; @@ -95,12 +100,14 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { SkImageInfo surfaceInfo = canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height()); tmpSurface = - SkSurface::MakeRenderTarget(directContext, skgpu::Budgeted::kYes, surfaceInfo); + SkSurfaces::RenderTarget(directContext, skgpu::Budgeted::kYes, surfaceInfo); tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT); GrGLFramebufferInfo fboInfo; - if (!tmpSurface->getBackendRenderTarget(SkSurface::kFlushWrite_BackendHandleAccess) - .getGLFramebufferInfo(&fboInfo)) { + if (!GrBackendRenderTargets::GetGLFramebufferInfo( + SkSurfaces::GetBackendRenderTarget( + tmpSurface.get(), SkSurfaces::BackendHandleAccess::kFlushWrite), + &fboInfo)) { ALOGW("Unable to extract renderTarget info from offscreen canvas; aborting GLFunctor"); return; } @@ -163,7 +170,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { // GL ops get inserted here if previous flush is missing, which could dirty the stencil bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(tmpCanvas); - tmpCanvas->flush(); // need this flush for the single op that draws into the stencil + directContext->flushAndSubmit(); // need this flush for the single op that draws into the stencil // ensure that the framebuffer that the webview will render into is bound before after we // draw into the stencil diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index da4f66d45a70..2b2e3995d17e 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -15,21 +15,25 @@ */ #include "RenderNodeDrawable.h" + #include <SkPaint.h> #include <SkPaintFilterCanvas.h> #include <SkPoint.h> #include <SkRRect.h> #include <SkRect.h> #include <gui/TraceUtils.h> +#include <include/effects/SkImageFilters.h> +#ifdef __ANDROID__ +#include <include/gpu/ganesh/SkImageGanesh.h> +#endif + +#include <optional> + #include "RenderNode.h" #include "SkiaDisplayList.h" #include "StretchMask.h" #include "TransformCanvas.h" -#include <include/effects/SkImageFilters.h> - -#include <optional> - namespace android { namespace uirenderer { namespace skiapipeline { @@ -255,9 +259,19 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot(); if (imageFilter) { auto subset = SkIRect::MakeWH(srcBounds.width(), srcBounds.height()); - snapshotImage = snapshotImage->makeWithFilter(recordingContext, imageFilter, - subset, clipBounds.roundOut(), - &srcBounds, &offset); + +#ifdef __ANDROID__ + if (recordingContext) { + snapshotImage = SkImages::MakeWithFilter( + recordingContext, snapshotImage, imageFilter, subset, + clipBounds.roundOut(), &srcBounds, &offset); + } else +#endif + { + snapshotImage = SkImages::MakeWithFilter(snapshotImage, imageFilter, subset, + clipBounds.roundOut(), &srcBounds, + &offset); + } } } else { const auto snapshotResult = renderNode->updateSnapshotIfRequired( @@ -362,7 +376,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { } void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas, - float* alphaMultiplier) { + float* alphaMultiplier, bool ignoreLayer) { if (properties.getLeft() != 0 || properties.getTop() != 0) { canvas->translate(properties.getLeft(), properties.getTop()); } @@ -378,7 +392,8 @@ void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, S canvas->concat(*properties.getTransformMatrix()); } } - if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale) { + if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale && + !ignoreLayer) { const StretchEffect& stretch = properties.layerProperties().getStretchEffect(); if (!stretch.isEmpty()) { canvas->concat( @@ -388,10 +403,10 @@ void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, S const bool isLayer = properties.effectiveLayerType() != LayerType::None; int clipFlags = properties.getClippingFlags(); if (properties.getAlpha() < 1) { - if (isLayer) { + if (isLayer && !ignoreLayer) { clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer } - if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) { + if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering()) || ignoreLayer) { *alphaMultiplier = properties.getAlpha(); } else { // savelayer needed to create an offscreen buffer diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h index c7582e734009..818ac45bf346 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h @@ -120,7 +120,7 @@ private: * Applies the rendering properties of a view onto a SkCanvas. */ static void setViewProperties(const RenderProperties& properties, SkCanvas* canvas, - float* alphaMultiplier); + float* alphaMultiplier, bool ignoreLayer = false); /** * Stores transform on the canvas at time of recording and is used for @@ -149,6 +149,11 @@ private: * display list that is searched for any render nodes with getProjectBackwards==true */ SkiaDisplayList* mProjectedDisplayList = nullptr; + + /** + * Allow BackdropFilterDrawable to apply same render properties onto SkCanvas. + */ + friend class BackdropFilterDrawable; }; } // namespace skiapipeline diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp index 11977bd54c2c..136740c799dd 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -27,7 +27,6 @@ #include <SkRect.h> #include <SkScalar.h> #include <SkShadowUtils.h> -#include <include/private/SkShadowFlags.h> namespace android { namespace uirenderer { diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h index 74955503dbb1..6ccb212fe6ca 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.h +++ b/libs/hwui/pipeline/skia/ShaderCache.h @@ -26,6 +26,7 @@ #include <string> #include <vector> +class GrDirectContext; class SkData; namespace android { diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index af2d3b34bac7..5c8285a8e1e9 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -66,6 +66,12 @@ void SkiaDisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) } } +void SkiaDisplayList::visit(std::function<void(const RenderNode&)> func) const { + for (auto& child : mChildNodes) { + child.getRenderNode()->visit(func); + } +} + static bool intersects(const SkISize screenSize, const Matrix4& mat, const SkRect& bounds) { Vector3 points[] = { Vector3 {bounds.fLeft, bounds.fTop, 0}, Vector3 {bounds.fRight, bounds.fTop, 0}, diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index 7af31a4dc4c6..e5bd5c9b2a3b 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -145,6 +145,8 @@ public: */ void updateChildren(std::function<void(RenderNode*)> updateFn); + void visit(std::function<void(const RenderNode&)> func) const; + /** * Returns true if there is a child render node that is a projection receiver. */ diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index cf31173d266e..774478669058 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -16,6 +16,9 @@ #include "SkiaOpenGLPipeline.h" +#include <include/gpu/ganesh/SkSurfaceGanesh.h> +#include <include/gpu/ganesh/gl/GrGLBackendSurface.h> +#include <include/gpu/gl/GrGLTypes.h> #include <GrBackendSurface.h> #include <SkBlendMode.h> #include <SkImageInfo.h> @@ -138,7 +141,8 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( LOG_ALWAYS_FATAL("Unsupported color type."); } - GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo); + auto backendRT = GrBackendRenderTargets::MakeGL(frame.width(), frame.height(), 0, + STENCIL_BUFFER_SIZE, fboInfo); SkSurfaceProps props(mColorMode == ColorMode::Default ? 0 : SkSurfaceProps::kAlwaysDither_Flag, kUnknown_SkPixelGeometry); @@ -150,9 +154,9 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( surface = getBufferSkSurface(bufferParams); preTransform = bufferParams.getTransform(); } else { - surface = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(), backendRT, - getSurfaceOrigin(), colorType, - mSurfaceColorSpace, &props); + surface = SkSurfaces::WrapBackendRenderTarget(mRenderThread.getGrContext(), backendRT, + getSurfaceOrigin(), colorType, + mSurfaceColorSpace, &props); preTransform = SkMatrix::I(); } @@ -174,7 +178,7 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( { ATRACE_NAME("flush commands"); - surface->flushAndSubmit(); + skgpu::ganesh::FlushAndSubmit(surface); } layerUpdateQueue->clear(); @@ -245,8 +249,7 @@ bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh if (mEglSurface != EGL_NO_SURFACE) { const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer); - const bool isPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); - ALOGE_IF(preserveBuffer != isPreserved, "Unable to match the desired swap behavior."); + mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); return true; } diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index cb23bcc166c8..e0f1f6ef44be 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -16,14 +16,16 @@ #include "SkiaPipeline.h" +#include <include/android/SkSurfaceAndroid.h> +#include <include/gpu/ganesh/SkSurfaceGanesh.h> +#include <include/encode/SkPngEncoder.h> #include <SkCanvas.h> #include <SkColor.h> #include <SkColorSpace.h> #include <SkData.h> #include <SkImage.h> -#include <SkImageEncoder.h> +#include <SkImageAndroid.h> #include <SkImageInfo.h> -#include <SkImagePriv.h> #include <SkMatrix.h> #include <SkMultiPictureDocument.h> #include <SkOverdrawCanvas.h> @@ -75,7 +77,7 @@ bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) { return false; } for (SkImage* image : mutableImages) { - if (SkImage_pinAsTexture(image, mRenderThread.getGrContext())) { + if (skgpu::ganesh::PinAsTexture(mRenderThread.getGrContext(), image)) { mPinnedImages.emplace_back(sk_ref_sp(image)); } else { return false; @@ -86,7 +88,7 @@ bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) { void SkiaPipeline::unpinImages() { for (auto& image : mPinnedImages) { - SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext()); + skgpu::ganesh::UnpinTexture(mRenderThread.getGrContext(), image.get()); } mPinnedImages.clear(); } @@ -187,9 +189,9 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator kPremul_SkAlphaType, getSurfaceColorSpace()); SkSurfaceProps props(0, kUnknown_SkPixelGeometry); SkASSERT(mRenderThread.getGrContext() != nullptr); - node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), - skgpu::Budgeted::kYes, info, 0, - this->getSurfaceOrigin(), &props)); + node->setLayerSurface(SkSurfaces::RenderTarget(mRenderThread.getGrContext(), + skgpu::Budgeted::kYes, info, 0, + this->getSurfaceOrigin(), &props)); if (node->getLayerSurface()) { // update the transform in window of the layer to reset its origin wrt light source // position @@ -222,8 +224,8 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height()); auto image = bitmap->makeImage(); if (image.get()) { - SkImage_pinAsTexture(image.get(), context); - SkImage_unpinAsTexture(image.get(), context); + skgpu::ganesh::PinAsTexture(context, image.get()); + skgpu::ganesh::UnpinTexture(context, image.get()); // A submit is necessary as there may not be a frame coming soon, so without a call // to submit these texture uploads can just sit in the queue building up until // we run out of RAM @@ -439,6 +441,13 @@ void SkiaPipeline::endCapture(SkSurface* surface) { procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){ return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData); }; + procs.fImageProc = [](SkImage* img, void* ctx) -> sk_sp<SkData> { + GrDirectContext* dCtx = static_cast<GrDirectContext*>(ctx); + return SkPngEncoder::Encode(dCtx, + img, + SkPngEncoder::Options{}); + }; + procs.fImageCtx = mRenderThread.getGrContext(); auto data = picture->serialize(&procs); savePictureAsync(data, mCapturedFile); mCaptureSequence = 0; @@ -621,7 +630,7 @@ sk_sp<SkSurface> SkiaPipeline::getBufferSkSurface( auto bufferColorSpace = bufferParams.getColorSpace(); if (mBufferSurface == nullptr || mBufferColorSpace == nullptr || !SkColorSpace::Equals(mBufferColorSpace.get(), bufferColorSpace.get())) { - mBufferSurface = SkSurface::MakeFromAHardwareBuffer( + mBufferSurface = SkSurfaces::WrapAndroidHardwareBuffer( mRenderThread.getGrContext(), mHardwareBuffer, kTopLeft_GrSurfaceOrigin, bufferColorSpace, nullptr, true); mBufferColorSpace = bufferColorSpace; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 3ca7eeb37a89..e917f9a66917 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -37,6 +37,7 @@ #include "NinePatchUtils.h" #include "RenderNode.h" #include "pipeline/skia/AnimatedDrawables.h" +#include "pipeline/skia/BackdropFilterDrawable.h" #ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc. #include "pipeline/skia/GLFunctorDrawable.h" #include "pipeline/skia/VkFunctorDrawable.h" @@ -168,6 +169,14 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { // Put Vulkan WebViews with non-rectangular clips in a HW layer renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex()); } + + // draw backdrop filter drawable if needed. + if (renderNode->stagingProperties().layerProperties().getBackdropImageFilter()) { + auto* backdropFilterDrawable = + mDisplayList->allocateDrawable<BackdropFilterDrawable>(renderNode, asSkCanvas()); + drawDrawable(backdropFilterDrawable); + } + drawDrawable(&renderNodeDrawable); // use staging property, since recording on UI thread @@ -227,6 +236,17 @@ void SkiaRecordingCanvas::handleMutableImages(Bitmap& bitmap, DrawImagePayload& } } +void SkiaRecordingCanvas::onFilterPaint(android::Paint& paint) { + INHERITED::onFilterPaint(paint); + SkShader* shader = paint.getShader(); + // TODO(b/264559422): This only works for very specifically a BitmapShader. + // It's better than nothing, though + SkImage* image = shader ? shader->isAImage(nullptr, nullptr) : nullptr; + if (image) { + mDisplayList->mMutableImages.push_back(image); + } +} + void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { auto payload = DrawImagePayload(bitmap); diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index a8e4580dc200..3bd091df1ece 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -105,6 +105,8 @@ private: void handleMutableImages(Bitmap& bitmap, DrawImagePayload& payload); + void onFilterPaint(Paint& paint) override; + using INHERITED = SkiaCanvas; }; diff --git a/libs/hwui/pipeline/skia/StretchMask.cpp b/libs/hwui/pipeline/skia/StretchMask.cpp index cad3703d8d2b..1676787ef671 100644 --- a/libs/hwui/pipeline/skia/StretchMask.cpp +++ b/libs/hwui/pipeline/skia/StretchMask.cpp @@ -18,14 +18,13 @@ #include "SkBlendMode.h" #include "SkCanvas.h" #include "SkSurface.h" -#include "include/gpu/GpuTypes.h" // from Skia #include "TransformCanvas.h" #include "SkiaDisplayList.h" using android::uirenderer::StretchMask; -void StretchMask::draw(GrRecordingContext* context, +void StretchMask::draw(GrRecordingContext*, const StretchEffect& stretch, const SkRect& bounds, skiapipeline::SkiaDisplayList* displayList, @@ -35,16 +34,14 @@ void StretchMask::draw(GrRecordingContext* context, if (mMaskSurface == nullptr || mMaskSurface->width() != width || mMaskSurface->height() != height) { // Create a new surface if we don't have one or our existing size does - // not match. - mMaskSurface = SkSurface::MakeRenderTarget( - context, - skgpu::Budgeted::kYes, - SkImageInfo::Make( - width, - height, - SkColorType::kAlpha_8_SkColorType, - SkAlphaType::kPremul_SkAlphaType) - ); + // not match. SkCanvas::makeSurface returns a new surface that will + // be GPU-backed if canvas was also. + mMaskSurface = canvas->makeSurface(SkImageInfo::Make( + width, + height, + SkColorType::kAlpha_8_SkColorType, + SkAlphaType::kPremul_SkAlphaType + )); mIsDirty = true; } @@ -53,7 +50,7 @@ void StretchMask::draw(GrRecordingContext* context, // Make sure to apply target transformation to the mask canvas // to ensure the replayed drawing commands generate the same result auto previousMatrix = displayList->mParentMatrix; - displayList->mParentMatrix = maskCanvas->getTotalMatrix(); + displayList->mParentMatrix = maskCanvas->getLocalToDeviceAs3x3(); maskCanvas->save(); maskCanvas->drawColor(0, SkBlendMode::kClear); TransformCanvas transformCanvas(maskCanvas, SkBlendMode::kSrcOver); diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp index adf3c06b8624..475b110604e4 100644 --- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp @@ -35,6 +35,8 @@ #include "effects/GainmapRenderer.h" #include <SkBlendMode.h> +#include <SkImage.h> +#include <SkImageAndroid.h> namespace android { namespace uirenderer { @@ -183,9 +185,9 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { // drawing into the offscreen surface, so we need to reset it here. canvas->resetMatrix(); - auto functorImage = SkImage::MakeFromAHardwareBuffer(mFrameBuffer.get(), kPremul_SkAlphaType, - canvas->imageInfo().refColorSpace(), - kBottomLeft_GrSurfaceOrigin); + auto functorImage = SkImages::DeferredFromAHardwareBuffer( + mFrameBuffer.get(), kPremul_SkAlphaType, canvas->imageInfo().refColorSpace(), + kBottomLeft_GrSurfaceOrigin); canvas->drawImage(functorImage, 0, 0, SkSamplingOptions(), &paint); canvas->restore(); } diff --git a/libs/hwui/private/hwui/DrawGlInfo.h b/libs/hwui/private/hwui/DrawGlInfo.h index eb1f9304a5c8..ed3fabc61708 100644 --- a/libs/hwui/private/hwui/DrawGlInfo.h +++ b/libs/hwui/private/hwui/DrawGlInfo.h @@ -24,8 +24,7 @@ namespace android { namespace uirenderer { /** - * Structure used by OpenGLRenderer::callDrawGLFunction() to pass and - * receive data from OpenGL functors. + * Structure used to pass and receive data from OpenGL functors. */ struct DrawGlInfo { // Input: current clip rect diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index babce88b8e1e..30d461271c89 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -17,6 +17,7 @@ #include "CacheManager.h" #include <GrContextOptions.h> +#include <GrTypes.h> #include <SkExecutor.h> #include <SkGraphics.h> #include <math.h> @@ -110,13 +111,18 @@ void CacheManager::configureContext(GrContextOptions* contextOptions, const void contextOptions->fPersistentCache = &cache; } +static GrPurgeResourceOptions toSkiaEnum(bool scratchOnly) { + return scratchOnly ? GrPurgeResourceOptions::kScratchResourcesOnly : + GrPurgeResourceOptions::kAllResources; +} + void CacheManager::trimMemory(TrimLevel mode) { if (!mGrContext) { return; } // flush and submit all work to the gpu and wait for it to finish - mGrContext->flushAndSubmit(/*syncCpu=*/true); + mGrContext->flushAndSubmit(GrSyncCpu::kYes); switch (mode) { case TrimLevel::BACKGROUND: @@ -130,7 +136,7 @@ void CacheManager::trimMemory(TrimLevel mode) { // that have persistent data to be purged in LRU order. mGrContext->setResourceCacheLimit(mBackgroundResourceBytes); SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes); - mGrContext->purgeUnlockedResources(mMemoryPolicy.purgeScratchOnly); + mGrContext->purgeUnlockedResources(toSkiaEnum(mMemoryPolicy.purgeScratchOnly)); mGrContext->setResourceCacheLimit(mMaxResourceBytes); SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes); break; @@ -150,7 +156,7 @@ void CacheManager::trimCaches(CacheTrimLevel mode) { case CacheTrimLevel::ALL_CACHES: SkGraphics::PurgeAllCaches(); if (mGrContext) { - mGrContext->purgeUnlockedResources(false); + mGrContext->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources); } break; default: @@ -163,7 +169,8 @@ void CacheManager::trimStaleResources() { return; } mGrContext->flushAndSubmit(); - mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30)); + mGrContext->performDeferredCleanup(std::chrono::seconds(30), + GrPurgeResourceOptions::kAllResources); } void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) { @@ -277,14 +284,15 @@ void CacheManager::onThreadIdle() { const nsecs_t now = systemTime(CLOCK_MONOTONIC); // Rate limiting - if ((now - mLastDeferredCleanup) < 25_ms) { + if ((now - mLastDeferredCleanup) > 25_ms) { mLastDeferredCleanup = now; const nsecs_t frameCompleteNanos = mFrameCompletions[0]; const nsecs_t frameDiffNanos = now - frameCompleteNanos; const nsecs_t cleanupMillis = - ns2ms(std::max(frameDiffNanos, mMemoryPolicy.minimumResourceRetention)); + ns2ms(std::clamp(frameDiffNanos, mMemoryPolicy.minimumResourceRetention, + mMemoryPolicy.maximumResourceRetention)); mGrContext->performDeferredCleanup(std::chrono::milliseconds(cleanupMillis), - mMemoryPolicy.purgeScratchOnly); + toSkiaEnum(mMemoryPolicy.purgeScratchOnly)); } } diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index 5e43ac209696..bcfa4f359d83 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -64,12 +64,13 @@ public: void unregisterCanvasContext(CanvasContext* context); void onContextStopped(CanvasContext* context); + bool areAllContextsStopped(); + private: friend class RenderThread; explicit CacheManager(RenderThread& thread); void setupCacheLimits(); - bool areAllContextsStopped(); void checkUiHidden(); void scheduleDestroyContext(); void cancelDestroyContext(); diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index f5b3ca602469..14602ef926d3 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -123,8 +123,9 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos()) , mContentDrawBounds(0, 0, 0, 0) , mRenderPipeline(std::move(renderPipeline)) - , mHintSessionWrapper(uiThreadId, renderThreadId) { + , mHintSessionWrapper(std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId)) { mRenderThread.cacheManager().registerCanvasContext(this); + mRenderThread.renderState().registerContextCallback(this); rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); mProfiler.setDensity(DeviceInfo::getDensity()); @@ -137,6 +138,8 @@ CanvasContext::~CanvasContext() { } mRenderNodes.clear(); mRenderThread.cacheManager().unregisterCanvasContext(this); + mRenderThread.renderState().removeContextCallback(this); + mHintSessionWrapper->destroy(); } void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) { @@ -160,6 +163,7 @@ void CanvasContext::destroy() { destroyHardwareResources(); mAnimationContext->destroy(); mRenderThread.cacheManager().onContextStopped(this); + mHintSessionWrapper->delayedDestroy(mRenderThread, 2_s, mHintSessionWrapper); } static void setBufferCount(ANativeWindow* window) { @@ -356,8 +360,9 @@ bool CanvasContext::makeCurrent() { return true; } -static bool wasSkipped(FrameInfo* info) { - return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame); +static std::optional<SkippedFrameReason> wasSkipped(FrameInfo* info) { + if (info) return info->getSkippedFrameReason(); + return std::nullopt; } bool CanvasContext::isSwapChainStuffed() { @@ -406,13 +411,26 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy // If the previous frame was dropped we don't need to hold onto it, so // just keep using the previous frame's structure instead - if (wasSkipped(mCurrentFrameInfo)) { + if (const auto reason = wasSkipped(mCurrentFrameInfo)) { // Use the oldest skipped frame in case we skip more than a single frame if (!mSkippedFrameInfo) { - mSkippedFrameInfo.emplace(); - mSkippedFrameInfo->vsyncId = - mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId); - mSkippedFrameInfo->startTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime); + switch (*reason) { + case SkippedFrameReason::AlreadyDrawn: + case SkippedFrameReason::NoBuffer: + case SkippedFrameReason::NoOutputTarget: + mSkippedFrameInfo.emplace(); + mSkippedFrameInfo->vsyncId = + mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId); + mSkippedFrameInfo->startTime = + mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime); + break; + case SkippedFrameReason::DrawingOff: + case SkippedFrameReason::ContextIsStopped: + case SkippedFrameReason::NothingToDraw: + // Do not report those as skipped frames as there was no frame expected to be + // drawn + break; + } } } else { mCurrentFrameInfo = mJankTracker.startFrame(); @@ -426,7 +444,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy info.damageAccumulator = &mDamageAccumulator; info.layerUpdateQueue = &mLayerUpdateQueue; info.damageGenerationId = mDamageId++; - info.out.canDrawThisFrame = true; + info.out.skippedFrameReason = std::nullopt; mAnimationContext->startFrame(info.mode); for (const sp<RenderNode>& node : mRenderNodes) { @@ -446,8 +464,8 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy mIsDirty = true; if (CC_UNLIKELY(!hasOutputTarget())) { - mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); - info.out.canDrawThisFrame = false; + info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget; + mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason); return; } @@ -462,23 +480,23 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy if (vsyncDelta < 2_ms) { // Already drew for this vsync pulse, UI draw request missed // the deadline for RT animations - info.out.canDrawThisFrame = false; + info.out.skippedFrameReason = SkippedFrameReason::AlreadyDrawn; } } else { - info.out.canDrawThisFrame = true; + info.out.skippedFrameReason = std::nullopt; } // TODO: Do we need to abort out if the backdrop is added but not ready? Should that even // be an allowable combination? if (mRenderNodes.size() > 2 && !mRenderNodes[1]->isRenderable()) { - info.out.canDrawThisFrame = false; + info.out.skippedFrameReason = SkippedFrameReason::NothingToDraw; } - if (info.out.canDrawThisFrame) { + if (!info.out.skippedFrameReason) { int err = mNativeSurface->reserveNext(); if (err != OK) { - mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); - info.out.canDrawThisFrame = false; + info.out.skippedFrameReason = SkippedFrameReason::NoBuffer; + mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason); ALOGW("reserveNext failed, error = %d (%s)", err, strerror(-err)); if (err != TIMED_OUT) { // A timed out surface can still recover, but assume others are permanently dead. @@ -487,11 +505,11 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy } } } else { - mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); + mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason); } bool postedFrameCallback = false; - if (info.out.hasAnimations || !info.out.canDrawThisFrame) { + if (info.out.hasAnimations || info.out.skippedFrameReason) { if (CC_UNLIKELY(!Properties::enableRTAnimations)) { info.out.requiresUiRedraw = true; } @@ -557,9 +575,20 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { mSyncDelayDuration = 0; mIdleDuration = 0; - if (!Properties::isDrawingEnabled() || - (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) { - mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); + const auto skippedFrameReason = [&]() -> std::optional<SkippedFrameReason> { + if (!Properties::isDrawingEnabled()) { + return SkippedFrameReason::DrawingOff; + } + + if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) { + return SkippedFrameReason::NothingToDraw; + } + + return std::nullopt; + }(); + if (skippedFrameReason) { + mCurrentFrameInfo->setSkippedFrameReason(*skippedFrameReason); + if (auto grContext = getGrContext()) { // Submit to ensure that any texture uploads complete and Skia can // free its staging buffers. @@ -739,7 +768,7 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline); int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration); - mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync); + mHintSessionWrapper->updateTargetWorkDuration(frameDeadline - intendedVsync); if (didDraw) { int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime); @@ -747,7 +776,7 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { int64_t actualDuration = frameDuration - (std::min(syncDelayDuration, mLastDequeueBufferDuration)) - dequeueBufferDuration - idleDuration; - mHintSessionWrapper.reportActualWorkDuration(actualDuration); + mHintSessionWrapper->reportActualWorkDuration(actualDuration); } mLastDequeueBufferDuration = dequeueBufferDuration; @@ -890,10 +919,10 @@ const SkM44& CanvasContext::getPixelSnapMatrix() const { } void CanvasContext::prepareAndDraw(RenderNode* node) { - ATRACE_CALL(); + int64_t vsyncId = mRenderThread.timeLord().lastVsyncId(); + ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId); nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos(); - int64_t vsyncId = mRenderThread.timeLord().lastVsyncId(); int64_t frameDeadline = mRenderThread.timeLord().lastFrameDeadline(); int64_t frameInterval = mRenderThread.timeLord().frameIntervalNanos(); int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; @@ -903,7 +932,7 @@ void CanvasContext::prepareAndDraw(RenderNode* node) { TreeInfo info(TreeInfo::MODE_RT_ONLY, *this); prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node); - if (info.out.canDrawThisFrame) { + if (!info.out.skippedFrameReason) { draw(info.out.solelyTextureViewUpdates); } else { // wait on fences so tasks don't overlap next frame @@ -964,6 +993,10 @@ void CanvasContext::destroyHardwareResources() { } } +void CanvasContext::onContextDestroyed() { + destroyHardwareResources(); +} + DeferredLayerUpdater* CanvasContext::createTextureLayer() { return mRenderPipeline->createTextureLayer(); } @@ -1081,11 +1114,11 @@ void CanvasContext::prepareSurfaceControlForWebview() { } void CanvasContext::sendLoadResetHint() { - mHintSessionWrapper.sendLoadResetHint(); + mHintSessionWrapper->sendLoadResetHint(); } void CanvasContext::sendLoadIncreaseHint() { - mHintSessionWrapper.sendLoadIncreaseHint(); + mHintSessionWrapper->sendLoadIncreaseHint(); } void CanvasContext::setSyncDelayDuration(nsecs_t duration) { @@ -1093,7 +1126,7 @@ void CanvasContext::setSyncDelayDuration(nsecs_t duration) { } void CanvasContext::startHintSession() { - mHintSessionWrapper.init(); + mHintSessionWrapper->init(); } bool CanvasContext::shouldDither() { @@ -1102,6 +1135,12 @@ bool CanvasContext::shouldDither() { return self->mColorMode != ColorMode::Default; } +void CanvasContext::visitAllRenderNodes(std::function<void(const RenderNode&)> func) const { + for (auto node : mRenderNodes) { + node->visit(func); + } +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 32ac5af94c14..37e4f7ecca54 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -43,6 +43,7 @@ #include "Lighting.h" #include "ReliableSurface.h" #include "RenderNode.h" +#include "renderstate/RenderState.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "utils/RingBuffer.h" @@ -64,7 +65,7 @@ class Frame; // This per-renderer class manages the bridge between the global EGL context // and the render surface. // TODO: Rename to Renderer or some other per-window, top-level manager -class CanvasContext : public IFrameCallback { +class CanvasContext : public IFrameCallback, public IGpuContextCallback { public: static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory, pid_t uiThreadId, @@ -154,6 +155,7 @@ public: void markLayerInUse(RenderNode* node); void destroyHardwareResources(); + void onContextDestroyed() override; DeferredLayerUpdater* createTextureLayer(); @@ -236,6 +238,8 @@ public: static bool shouldDither(); + void visitAllRenderNodes(std::function<void(const RenderNode&)>) const; + private: CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline, @@ -359,7 +363,7 @@ private: std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback; std::function<void()> mPrepareSurfaceControlForWebviewCallback; - HintSessionWrapper mHintSessionWrapper; + std::shared_ptr<HintSessionWrapper> mHintSessionWrapper; nsecs_t mLastDequeueBufferDuration = 0; nsecs_t mSyncDelayDuration = 0; nsecs_t mIdleDuration = 0; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 53b43ba417d0..1b333bfccbf1 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -104,7 +104,7 @@ void DrawFrameTask::run() { info.forceDrawFrame = mForceDrawFrame; mForceDrawFrame = false; canUnblockUiThread = syncFrameState(info); - canDrawThisFrame = info.out.canDrawThisFrame; + canDrawThisFrame = !info.out.skippedFrameReason.has_value(); solelyTextureViewUpdates = info.out.solelyTextureViewUpdates; if (mFrameCommitCallback) { @@ -192,11 +192,12 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { if (CC_UNLIKELY(!hasTarget || !canDraw)) { if (!hasTarget) { mSyncResult |= SyncResult::LostSurfaceRewardIfFound; + info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget; } else { // If we have a surface but can't draw we must be stopped mSyncResult |= SyncResult::ContextIsStopped; + info.out.skippedFrameReason = SkippedFrameReason::ContextIsStopped; } - info.out.canDrawThisFrame = false; } if (info.out.hasAnimations) { @@ -204,7 +205,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { mSyncResult |= SyncResult::UIRedrawRequired; } } - if (!info.out.canDrawThisFrame) { + if (info.out.skippedFrameReason) { mSyncResult |= SyncResult::FrameDropped; } // If prepareTextures is false, we ran out of texture cache space diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp index b34da5153a72..2362331aca26 100644 --- a/libs/hwui/renderthread/HintSessionWrapper.cpp +++ b/libs/hwui/renderthread/HintSessionWrapper.cpp @@ -24,6 +24,7 @@ #include <vector> #include "../Properties.h" +#include "RenderThread.h" #include "thread/CommonPool.h" using namespace std::chrono_literals; @@ -62,24 +63,26 @@ HintSessionWrapper::~HintSessionWrapper() { } void HintSessionWrapper::destroy() { - if (mHintSessionFuture.valid()) { - mHintSession = mHintSessionFuture.get(); + if (mHintSessionFuture.has_value()) { + mHintSession = mHintSessionFuture->get(); + mHintSessionFuture = std::nullopt; } if (mHintSession) { mBinding->closeSession(mHintSession); mSessionValid = true; mHintSession = nullptr; } + mResetsSinceLastReport = 0; } bool HintSessionWrapper::init() { if (mHintSession != nullptr) return true; - // If we're waiting for the session - if (mHintSessionFuture.valid()) { + if (mHintSessionFuture.has_value()) { // If the session is here - if (mHintSessionFuture.wait_for(0s) == std::future_status::ready) { - mHintSession = mHintSessionFuture.get(); + if (mHintSessionFuture->wait_for(0s) == std::future_status::ready) { + mHintSession = mHintSessionFuture->get(); + mHintSessionFuture = std::nullopt; if (mHintSession != nullptr) { mSessionValid = true; return true; @@ -107,12 +110,13 @@ bool HintSessionWrapper::init() { tids.push_back(mUiThreadId); tids.push_back(mRenderThreadId); - // Use a placeholder target value to initialize, - // this will always be replaced elsewhere before it gets used - int64_t defaultTargetDurationNanos = 16666667; + // Use the cached target value if there is one, otherwise use a default. This is to ensure + // the cached target and target in PowerHAL are consistent, and that it updates correctly + // whenever there is a change. + int64_t targetDurationNanos = + mLastTargetWorkDuration == 0 ? kDefaultTargetDuration : mLastTargetWorkDuration; mHintSessionFuture = CommonPool::async([=, this, tids = std::move(tids)] { - return mBinding->createSession(manager, tids.data(), tids.size(), - defaultTargetDurationNanos); + return mBinding->createSession(manager, tids.data(), tids.size(), targetDurationNanos); }); return false; } @@ -136,6 +140,7 @@ void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) { actualDurationNanos < kSanityCheckUpperBound) { mBinding->reportActualWorkDuration(mHintSession, actualDurationNanos); } + mLastFrameNotification = systemTime(); } void HintSessionWrapper::sendLoadResetHint() { @@ -155,6 +160,27 @@ void HintSessionWrapper::sendLoadIncreaseHint() { mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)); } +bool HintSessionWrapper::alive() { + return mHintSession != nullptr; +} + +nsecs_t HintSessionWrapper::getLastUpdate() { + return mLastFrameNotification; +} + +// Requires passing in its shared_ptr since it shouldn't own a shared_ptr to itself +void HintSessionWrapper::delayedDestroy(RenderThread& rt, nsecs_t delay, + std::shared_ptr<HintSessionWrapper> wrapperPtr) { + nsecs_t lastUpdate = wrapperPtr->getLastUpdate(); + rt.queue().postDelayed(delay, [lastUpdate = lastUpdate, wrapper = wrapperPtr]() mutable { + if (wrapper->getLastUpdate() == lastUpdate) { + wrapper->destroy(); + } + // Ensure the shared_ptr is killed at the end of the method + wrapper = nullptr; + }); +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h index f8b876e28d51..41891cd80a42 100644 --- a/libs/hwui/renderthread/HintSessionWrapper.h +++ b/libs/hwui/renderthread/HintSessionWrapper.h @@ -19,6 +19,7 @@ #include <android/performance_hint.h> #include <future> +#include <optional> #include "utils/TimeUtils.h" @@ -27,6 +28,8 @@ namespace uirenderer { namespace renderthread { +class RenderThread; + class HintSessionWrapper { public: friend class HintSessionWrapperTests; @@ -40,10 +43,15 @@ public: void sendLoadIncreaseHint(); bool init(); void destroy(); + bool alive(); + nsecs_t getLastUpdate(); + void delayedDestroy(renderthread::RenderThread& rt, nsecs_t delay, + std::shared_ptr<HintSessionWrapper> wrapperPtr); private: APerformanceHintSession* mHintSession = nullptr; - std::future<APerformanceHintSession*> mHintSessionFuture; + // This needs to work concurrently for testing + std::optional<std::shared_future<APerformanceHintSession*>> mHintSessionFuture; int mResetsSinceLastReport = 0; nsecs_t mLastFrameNotification = 0; @@ -57,6 +65,7 @@ private: static constexpr nsecs_t kResetHintTimeout = 100_ms; static constexpr int64_t kSanityCheckLowerBound = 100_us; static constexpr int64_t kSanityCheckUpperBound = 10_s; + static constexpr int64_t kDefaultTargetDuration = 16666667; // Allows easier stub when testing class HintSessionBinding { diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 224c878bf43d..be163bad77a7 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -16,7 +16,13 @@ #include "RenderProxy.h" +#include <SkBitmap.h> +#include <SkImage.h> +#include <SkPicture.h> #include <gui/TraceUtils.h> +#include <pthread.h> +#include <ui/GraphicBufferAllocator.h> + #include "DeferredLayerUpdater.h" #include "DisplayList.h" #include "Properties.h" @@ -29,12 +35,6 @@ #include "utils/Macros.h" #include "utils/TimeUtils.h" -#include <SkBitmap.h> -#include <SkImage.h> -#include <SkPicture.h> - -#include <pthread.h> - namespace android { namespace uirenderer { namespace renderthread { @@ -323,6 +323,11 @@ void RenderProxy::dumpGraphicsMemory(int fd, bool includeProfileData, bool reset } }); } + if (!Properties::isolatedProcess) { + std::string grallocInfo; + GraphicBufferAllocator::getInstance().dump(grallocInfo); + dprintf(fd, "%s\n", grallocInfo.c_str()); + } } void RenderProxy::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) { diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 0dea941c91f9..f76ea063842f 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -17,6 +17,7 @@ #include "RenderThread.h" #include <GrContextOptions.h> +#include <include/gpu/ganesh/gl/GrGLDirectContext.h> #include <android-base/properties.h> #include <dlfcn.h> #include <gl/GrGLInterface.h> @@ -286,7 +287,7 @@ void RenderThread::requireGlContext() { auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION)); auto size = glesVersion ? strlen(glesVersion) : -1; cacheManager().configureContext(&options, glesVersion, size); - sk_sp<GrDirectContext> grContext(GrDirectContext::MakeGL(std::move(glInterface), options)); + sk_sp<GrDirectContext> grContext(GrDirectContexts::MakeGL(std::move(glInterface), options)); LOG_ALWAYS_FATAL_IF(!grContext.get()); setGrContext(grContext); } @@ -357,7 +358,15 @@ void RenderThread::dumpGraphicsMemory(int fd, bool includeProfileData) { String8 cachesOutput; mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState); - dprintf(fd, "\nPipeline=%s\n%s\n", pipelineToString(), cachesOutput.c_str()); + dprintf(fd, "\nPipeline=%s\n%s", pipelineToString(), cachesOutput.c_str()); + for (auto&& context : mCacheManager->mCanvasContexts) { + context->visitAllRenderNodes([&](const RenderNode& node) { + if (node.isTextureView()) { + dprintf(fd, "TextureView: %dx%d\n", node.getWidth(), node.getHeight()); + } + }); + } + dprintf(fd, "\n"); } void RenderThread::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) { diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 46698a6fdcc0..22c586248705 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -24,6 +24,9 @@ #include <GrTypes.h> #include <android/sync.h> #include <gui/TraceUtils.h> +#include <include/gpu/ganesh/SkSurfaceGanesh.h> +#include <include/gpu/ganesh/vk/GrVkBackendSurface.h> +#include <include/gpu/ganesh/vk/GrVkDirectContext.h> #include <ui/FatVector.h> #include <vk/GrVkExtensions.h> #include <vk/GrVkTypes.h> @@ -33,9 +36,6 @@ #include "pipeline/skia/ShaderCache.h" #include "renderstate/RenderState.h" -#undef LOG_TAG -#define LOG_TAG "VulkanManager" - namespace android { namespace uirenderer { namespace renderthread { @@ -386,25 +386,23 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe } void VulkanManager::initialize() { - std::lock_guard _lock{mInitializeLock}; - - if (mDevice != VK_NULL_HANDLE) { - return; - } + std::call_once(mInitFlag, [&] { + GET_PROC(EnumerateInstanceVersion); + uint32_t instanceVersion; + LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion)); + LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0)); - GET_PROC(EnumerateInstanceVersion); - uint32_t instanceVersion; - LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion)); - LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0)); + this->setupDevice(mExtensions, mPhysicalDeviceFeatures2); - this->setupDevice(mExtensions, mPhysicalDeviceFeatures2); + mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); + mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue); - mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); - mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue); + if (Properties::enablePartialUpdates && Properties::useBufferAge) { + mSwapBehavior = SwapBehavior::BufferAge; + } - if (Properties::enablePartialUpdates && Properties::useBufferAge) { - mSwapBehavior = SwapBehavior::BufferAge; - } + mInitialized = true; + }); } static void onGrContextReleased(void* context) { @@ -438,7 +436,7 @@ sk_sp<GrDirectContext> VulkanManager::createContext(GrContextOptions& options, options.fContextDeleteContext = this; options.fContextDeleteProc = onGrContextReleased; - return GrDirectContext::MakeVulkan(backendContext, options); + return GrDirectContexts::MakeVulkan(backendContext, options); } VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const { @@ -518,7 +516,7 @@ Frame VulkanManager::dequeueNextBuffer(VulkanSurface* surface) { // The following flush blocks the GPU immediately instead of waiting for // other drawing ops. It seems dequeue_fence is not respected otherwise. // TODO: remove the flush after finding why backendSemaphore is not working. - bufferInfo->skSurface->flushAndSubmit(); + skgpu::ganesh::FlushAndSubmit(bufferInfo->skSurface.get()); } } } @@ -586,10 +584,10 @@ nsecs_t VulkanManager::finishFrame(SkSurface* surface) { } else { semaphore = VK_NULL_HANDLE; } - GrSemaphoresSubmitted submitted = - surface->flush(SkSurface::BackendSurfaceAccess::kPresent, flushInfo); GrDirectContext* context = GrAsDirectContext(surface->recordingContext()); ALOGE_IF(!context, "Surface is not backed by gpu"); + GrSemaphoresSubmitted submitted = context->flush( + surface, SkSurfaces::BackendSurfaceAccess::kPresent, flushInfo); context->submit(); const nsecs_t submissionTime = systemTime(); if (semaphore != VK_NULL_HANDLE) { @@ -599,10 +597,11 @@ nsecs_t VulkanManager::finishFrame(SkSurface* surface) { // retrieve VkImage used as render target VkImage image = VK_NULL_HANDLE; GrBackendRenderTarget backendRenderTarget = - surface->getBackendRenderTarget(SkSurface::kFlushRead_BackendHandleAccess); + SkSurfaces::GetBackendRenderTarget( + surface, SkSurfaces::BackendHandleAccess::kFlushRead); if (backendRenderTarget.isValid()) { GrVkImageInfo info; - if (backendRenderTarget.getVkImageInfo(&info)) { + if (GrBackendRenderTargets::GetVkImageInfo(backendRenderTarget, &info)) { image = info.fImage; } else { ALOGE("Frame boundary: backend is not vulkan"); diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 2be1ffdbc423..dbef7fbd51b2 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -70,7 +70,7 @@ public: void initialize(); // Quick check to see if the VulkanManager has been initialized. - bool hasVkContext() { return mDevice != VK_NULL_HANDLE; } + bool hasVkContext() { return mInitialized; } // Create and destroy functions for wrapping an ANativeWindow in a VulkanSurface VulkanSurface* createSurface(ANativeWindow* window, @@ -204,7 +204,8 @@ private: VkSemaphore mSwapSemaphore = VK_NULL_HANDLE; void* mDestroySemaphoreContext = nullptr; - std::mutex mInitializeLock; + std::once_flag mInitFlag; + std::atomic_bool mInitialized = false; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index 3168cb09291b..20b743bab2c2 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -16,6 +16,7 @@ #include "VulkanSurface.h" +#include <include/android/SkSurfaceAndroid.h> #include <GrDirectContext.h> #include <SkSurface.h> #include <algorithm> @@ -24,9 +25,6 @@ #include "VulkanManager.h" #include "utils/Color.h" -#undef LOG_TAG -#define LOG_TAG "VulkanSurface" - namespace android { namespace uirenderer { namespace renderthread { @@ -470,12 +468,12 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { surfaceProps = SkSurfaceProps(SkSurfaceProps::kAlwaysDither_Flag | surfaceProps.flags(), surfaceProps.pixelGeometry()); } - bufferInfo->skSurface = SkSurface::MakeFromAHardwareBuffer( + bufferInfo->skSurface = SkSurfaces::WrapAndroidHardwareBuffer( mGrContext, ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()), kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, &surfaceProps, /*from_window=*/true); if (bufferInfo->skSurface.get() == nullptr) { - ALOGE("SkSurface::MakeFromAHardwareBuffer failed"); + ALOGE("SkSurfaces::WrapAndroidHardwareBuffer failed"); mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, mNativeBuffers[idx].dequeue_fence.release()); mNativeBuffers[idx].dequeued = false; diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 81ecfe59d3bc..ffc664c2e1bc 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -61,18 +61,10 @@ namespace uirenderer { ADD_FAILURE() << "ClipState not a rect"; \ } -#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \ - TEST(test_case_name, test_name##_##pipeline) { \ - RenderPipelineType oldType = Properties::getRenderPipelineType(); \ - Properties::overrideRenderPipelineType(RenderPipelineType::pipeline); \ - functionCall; \ - Properties::overrideRenderPipelineType(oldType); \ - }; - -#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \ - INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \ - TestUtils::runOnRenderThread( \ - test_case_name##_##test_name##_RenderThreadTest::doTheThing)) +#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name) \ + TEST(test_case_name, test_name) { \ + TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \ + } /** * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope @@ -83,21 +75,7 @@ namespace uirenderer { public: \ static void doTheThing(renderthread::RenderThread& renderThread); \ }; \ - INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \ - /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \ - /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \ - void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \ - renderthread::RenderThread& renderThread) - -/** - * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes - */ -#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \ - class test_case_name##_##test_name##_RenderThreadTest { \ - public: \ - static void doTheThing(renderthread::RenderThread& renderThread); \ - }; \ - INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \ + INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name); \ /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \ /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \ void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \ @@ -307,13 +285,21 @@ public: int destroyed = 0; int removeOverlays = 0; int glesDraw = 0; + int vkInitialize = 0; + int vkDraw = 0; + int vkPostDraw = 0; }; static void expectOnRenderThread(const std::string_view& function = "unknown") { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()) << "Called on wrong thread: " << function; } - static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) { + static int createMockFunctor() { + const auto renderMode = WebViewFunctor_queryPlatformRenderMode(); + return WebViewFunctor_create(nullptr, createMockFunctorCallbacks(renderMode), renderMode); + } + + static WebViewFunctorCallbacks createMockFunctorCallbacks(RenderMode mode) { auto callbacks = WebViewFunctorCallbacks{ .onSync = [](int functor, void* client_data, const WebViewSyncData& data) { @@ -345,9 +331,22 @@ public: sMockFunctorCounts[functor].glesDraw++; }; break; - default: - ADD_FAILURE(); - return WebViewFunctorCallbacks{}; + case RenderMode::Vulkan: + callbacks.vk.initialize = [](int functor, void* data, + const VkFunctorInitParams& params) { + expectOnRenderThread("initialize"); + sMockFunctorCounts[functor].vkInitialize++; + }; + callbacks.vk.draw = [](int functor, void* data, const VkFunctorDrawParams& params, + const WebViewOverlayData& overlayParams) { + expectOnRenderThread("draw"); + sMockFunctorCounts[functor].vkDraw++; + }; + callbacks.vk.postDraw = [](int functor, void* data) { + expectOnRenderThread("postDraw"); + sMockFunctorCounts[functor].vkPostDraw++; + }; + break; } return callbacks; } diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp index f3f32eb6897c..e227999c2432 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -14,30 +14,32 @@ * limitations under the License. */ -#include "tests/common/LeakChecker.h" -#include "tests/common/TestScene.h" - -#include "Properties.h" -#include "hwui/Typeface.h" -#include "HardwareBitmapUploader.h" -#include "renderthread/RenderProxy.h" - +#include <android-base/parsebool.h> #include <benchmark/benchmark.h> +#include <errno.h> +#include <fcntl.h> #include <fnmatch.h> #include <getopt.h> #include <pthread.h> #include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> + +#include <regex> #include <string> #include <unordered_map> #include <vector> -#include <errno.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> +#include "HardwareBitmapUploader.h" +#include "Properties.h" +#include "hwui/Typeface.h" +#include "renderthread/RenderProxy.h" +#include "tests/common/LeakChecker.h" +#include "tests/common/TestScene.h" using namespace android; +using namespace android::base; using namespace android::uirenderer; using namespace android::uirenderer::test; @@ -69,6 +71,9 @@ OPTIONS: --onscreen Render tests on device screen. By default tests are offscreen rendered --benchmark_format Set output format. Possible values are tabular, json, csv + --benchmark_list_tests Lists the tests that would run but does not run them + --benchmark_filter=<regex> Filters the test set to the given regex. If prefixed with `-` and test + that doesn't match the given regex is run --renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk --skip-leak-check Skips the memory leak check --report-gpu-memory[=verbose] Dumps the GPU memory usage after each test run @@ -140,6 +145,9 @@ static bool setBenchmarkFormat(const char* format) { if (!strcmp(format, "tabular")) { gBenchmarkReporter.reset(new benchmark::ConsoleReporter()); } else if (!strcmp(format, "json")) { + // We cannot print the leak check if outputing to JSON as that will break + // JSON parsers since it's not JSON-formatted + gRunLeakCheck = false; gBenchmarkReporter.reset(new benchmark::JSONReporter()); } else { fprintf(stderr, "Unknown format '%s'\n", format); @@ -160,6 +168,24 @@ static bool setRenderer(const char* renderer) { return true; } +static void addTestsThatMatchFilter(std::string spec) { + if (spec.empty() || spec == "all") { + spec = "."; // Regexp that matches all benchmarks + } + bool isNegativeFilter = false; + if (spec[0] == '-') { + spec.replace(0, 1, ""); + isNegativeFilter = true; + } + std::regex re(spec, std::regex_constants::extended); + for (auto& iter : TestScene::testMap()) { + if ((isNegativeFilter && !std::regex_search(iter.first, re)) || + (!isNegativeFilter && std::regex_search(iter.first, re))) { + gRunTests.push_back(iter.second); + } + } +} + // For options that only exist in long-form. Anything in the // 0-255 range is reserved for short options (which just use their ASCII value) namespace LongOpts { @@ -170,6 +196,8 @@ enum { ReportFrametime, CpuSet, BenchmarkFormat, + BenchmarkListTests, + BenchmarkFilter, Onscreen, Offscreen, Renderer, @@ -179,14 +207,16 @@ enum { } static const struct option LONG_OPTIONS[] = { - {"frames", required_argument, nullptr, 'f'}, - {"repeat", required_argument, nullptr, 'r'}, + {"count", required_argument, nullptr, 'c'}, + {"runs", required_argument, nullptr, 'r'}, {"help", no_argument, nullptr, 'h'}, {"list", no_argument, nullptr, LongOpts::List}, {"wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu}, {"report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime}, {"cpuset", required_argument, nullptr, LongOpts::CpuSet}, {"benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat}, + {"benchmark_list_tests", optional_argument, nullptr, LongOpts::BenchmarkListTests}, + {"benchmark_filter", required_argument, nullptr, LongOpts::BenchmarkFilter}, {"onscreen", no_argument, nullptr, LongOpts::Onscreen}, {"offscreen", no_argument, nullptr, LongOpts::Offscreen}, {"renderer", required_argument, nullptr, LongOpts::Renderer}, @@ -197,8 +227,12 @@ static const struct option LONG_OPTIONS[] = { static const char* SHORT_OPTIONS = "c:r:h"; void parseOptions(int argc, char* argv[]) { + benchmark::BenchmarkReporter::Context::executable_name = (argc > 0) ? argv[0] : "unknown"; + int c; bool error = false; + bool listTestsOnly = false; + bool testsAreFiltered = false; opterr = 0; while (true) { @@ -272,6 +306,21 @@ void parseOptions(int argc, char* argv[]) { } break; + case LongOpts::BenchmarkListTests: + if (!optarg || ParseBool(optarg) == ParseBoolResult::kTrue) { + listTestsOnly = true; + } + break; + + case LongOpts::BenchmarkFilter: + if (!optarg) { + error = true; + break; + } + addTestsThatMatchFilter(optarg); + testsAreFiltered = true; + break; + case LongOpts::Renderer: if (!optarg) { error = true; @@ -346,11 +395,18 @@ void parseOptions(int argc, char* argv[]) { } } } while (optind < argc); - } else { + } else if (gRunTests.empty() && !testsAreFiltered) { for (auto& iter : TestScene::testMap()) { gRunTests.push_back(iter.second); } } + + if (listTestsOnly) { + for (auto& iter : gRunTests) { + std::cout << iter.name << std::endl; + } + exit(EXIT_SUCCESS); + } } int main(int argc, char* argv[]) { diff --git a/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp b/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp index 138b3efd10ed..b8b3f0aa5229 100644 --- a/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp +++ b/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp @@ -46,7 +46,7 @@ RENDERTHREAD_TEST(AutoBackendTextureRelease, makeImage_invalid) { EXPECT_EQ(1, TestUtils::getUsageCount(textureRelease)); - // SkImage::MakeFromTexture should fail if given null GrDirectContext. + // SkImages::BorrowTextureFrom should fail if given null GrDirectContext. textureRelease->makeImage(buffer, HAL_DATASPACE_UNKNOWN, /*context = */ nullptr); EXPECT_EQ(1, TestUtils::getUsageCount(textureRelease)); diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp index 2b90bda87ecd..89d00d3c0dcb 100644 --- a/libs/hwui/tests/unit/CacheManagerTests.cpp +++ b/libs/hwui/tests/unit/CacheManagerTests.cpp @@ -20,7 +20,8 @@ #include "renderthread/EglManager.h" #include "tests/common/TestUtils.h" -#include <SkImagePriv.h> +#include <SkImageAndroid.h> +#include <include/gpu/ganesh/SkSurfaceGanesh.h> #include "include/gpu/GpuTypes.h" // from Skia using namespace android; @@ -34,7 +35,7 @@ static size_t getCacheUsage(GrDirectContext* grContext) { } // TOOD(258700630): fix this test and re-enable -RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) { +RENDERTHREAD_TEST(CacheManager, DISABLED_trimMemory) { int32_t width = DeviceInfo::get()->getWidth(); int32_t height = DeviceInfo::get()->getHeight(); GrDirectContext* grContext = renderThread.getGrContext(); @@ -46,8 +47,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) { while (getCacheUsage(grContext) <= renderThread.cacheManager().getBackgroundCacheSize()) { SkImageInfo info = SkImageInfo::MakeA8(width, height); - sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(grContext, skgpu::Budgeted::kYes, - info); + sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(grContext, skgpu::Budgeted::kYes, + info); surface->getCanvas()->drawColor(SK_AlphaTRANSPARENT); grContext->flushAndSubmit(); @@ -57,9 +58,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) { // create an image and pin it so that we have something with a unique key in the cache sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(width, height)); - sk_sp<SkImage> image = bitmap->makeImage(); - ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext)); - + sk_sp<SkImage> image = bitmap->makeImage(); // calls skgpu::ganesh::PinAsTexture under the hood. + ASSERT_TRUE(skgpu::ganesh::PinAsTexture(grContext, image.get())); // attempt to trim all memory while we still hold strong refs renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE); ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes()); @@ -71,7 +71,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) { } // unpin the image which should add a unique purgeable key to the cache - SkImage_unpinAsTexture(image.get(), grContext); + skgpu::ganesh::UnpinTexture(grContext, image.get()); // verify that we have enough purgeable bytes const size_t purgeableBytes = grContext->getResourceCachePurgeableBytes(); diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp index 9e376e32f8ea..47a41057cac9 100644 --- a/libs/hwui/tests/unit/CanvasContextTests.cpp +++ b/libs/hwui/tests/unit/CanvasContextTests.cpp @@ -19,6 +19,7 @@ #include "AnimationContext.h" #include "IContextFactory.h" #include "renderthread/CanvasContext.h" +#include "renderthread/VulkanManager.h" #include "tests/common/TestUtils.h" using namespace android; @@ -42,3 +43,38 @@ RENDERTHREAD_TEST(CanvasContext, create) { canvasContext->destroy(); } + +RENDERTHREAD_TEST(CanvasContext, buildLayerDoesntLeak) { + auto node = TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(0xFFFF0000, SkBlendMode::kSrc); + }); + ASSERT_TRUE(node->isValid()); + EXPECT_EQ(LayerType::None, node->stagingProperties().effectiveLayerType()); + node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer); + + auto& cacheManager = renderThread.cacheManager(); + EXPECT_TRUE(cacheManager.areAllContextsStopped()); + ContextFactory contextFactory; + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, node.get(), &contextFactory, 0, 0)); + canvasContext->buildLayer(node.get()); + EXPECT_TRUE(node->hasLayer()); + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + auto instance = VulkanManager::peekInstance(); + if (instance) { + EXPECT_TRUE(instance->hasVkContext()); + } else { + ADD_FAILURE() << "VulkanManager wasn't initialized to buildLayer?"; + } + } + renderThread.destroyRenderingContext(); + EXPECT_FALSE(node->hasLayer()) << "Node still has a layer after rendering context destroyed"; + + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + auto instance = VulkanManager::peekInstance(); + if (instance) { + ADD_FAILURE() << "VulkanManager still exists"; + EXPECT_FALSE(instance->hasVkContext()); + } + } +} diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp index 0c389bfe8b71..cfa18ae01b4f 100644 --- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -40,7 +40,7 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { // push the deferred updates to the layer SkBitmap bitmap; bitmap.allocN32Pixels(16, 16); - sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap); + sk_sp<SkImage> layerImage = SkImages::RasterFromBitmap(bitmap); layerUpdater->updateLayer(true, layerImage, 0, SkRect::MakeEmpty()); // the backing layer should now have all the properties applied. diff --git a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp index 623be1e3f3f3..10a740a1f803 100644 --- a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp +++ b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp @@ -23,9 +23,11 @@ #include <chrono> #include "Properties.h" +#include "tests/common/TestUtils.h" using namespace testing; using namespace std::chrono_literals; +using namespace android::uirenderer::renderthread; APerformanceHintManager* managerPtr = reinterpret_cast<APerformanceHintManager*>(123); APerformanceHintSession* sessionPtr = reinterpret_cast<APerformanceHintSession*>(456); @@ -42,6 +44,9 @@ public: protected: std::shared_ptr<HintSessionWrapper> mWrapper; + std::promise<int> blockDestroyCallUntil; + std::promise<int> waitForDestroyFinished; + class MockHintSessionBinding : public HintSessionWrapper::HintSessionBinding { public: void init() override; @@ -53,11 +58,17 @@ protected: MOCK_METHOD(void, fakeUpdateTargetWorkDuration, (APerformanceHintSession*, int64_t)); MOCK_METHOD(void, fakeReportActualWorkDuration, (APerformanceHintSession*, int64_t)); MOCK_METHOD(void, fakeSendHint, (APerformanceHintSession*, int32_t)); + // Needs to be on the binding so it can be accessed from static methods + std::promise<int> allowCreationToFinish; }; // Must be static so it can have function pointers we can point to with static methods static std::shared_ptr<MockHintSessionBinding> sMockBinding; + static void allowCreationToFinish() { sMockBinding->allowCreationToFinish.set_value(1); } + void allowDelayedDestructionToStart() { blockDestroyCallUntil.set_value(1); } + void waitForDelayedDestructionToFinish() { waitForDestroyFinished.get_future().wait(); } + // Must be static so we can point to them as normal fn pointers with HintSessionBinding static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); }; static APerformanceHintSession* stubCreateSession(APerformanceHintManager* manager, @@ -65,6 +76,12 @@ protected: int64_t initialTarget) { return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget); } + static APerformanceHintSession* stubManagedCreateSession(APerformanceHintManager* manager, + const int32_t* ids, size_t idsSize, + int64_t initialTarget) { + sMockBinding->allowCreationToFinish.get_future().wait(); + return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget); + } static APerformanceHintSession* stubSlowCreateSession(APerformanceHintManager* manager, const int32_t* ids, size_t idsSize, int64_t initialTarget) { @@ -85,7 +102,21 @@ protected: static void stubSendHint(APerformanceHintSession* session, int32_t hintId) { sMockBinding->fakeSendHint(session, hintId); }; - void waitForWrapperReady() { mWrapper->mHintSessionFuture.wait(); } + void waitForWrapperReady() { + if (mWrapper->mHintSessionFuture.has_value()) { + mWrapper->mHintSessionFuture->wait(); + } + } + void scheduleDelayedDestroyManaged() { + TestUtils::runOnRenderThread([&](renderthread::RenderThread& rt) { + // Guaranteed to be scheduled first, allows destruction to start + rt.queue().postDelayed(0_ms, [&] { blockDestroyCallUntil.get_future().wait(); }); + // Guaranteed to be scheduled second, destroys the session + mWrapper->delayedDestroy(rt, 1_ms, mWrapper); + // This is guaranteed to be queued after the destroy, signals that destruction is done + rt.queue().postDelayed(1_ms, [&] { waitForDestroyFinished.set_value(1); }); + }); + } }; std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding> @@ -113,6 +144,7 @@ void HintSessionWrapperTests::MockHintSessionBinding::init() { } void HintSessionWrapperTests::TearDown() { + // Ensure that anything running on RT is completely finished mWrapper = nullptr; sMockBinding = nullptr; } @@ -122,6 +154,7 @@ TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) { sMockBinding->createSession = stubSlowCreateSession; mWrapper->init(); mWrapper = nullptr; + Mock::VerifyAndClearExpectations(sMockBinding.get()); } TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) { @@ -148,4 +181,162 @@ TEST_F(HintSessionWrapperTests, loadResetHintsSendCorrectly) { mWrapper->sendLoadResetHint(); } +TEST_F(HintSessionWrapperTests, delayedDeletionWorksCorrectlyAndOnlyClosesOnce) { + EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); + mWrapper->init(); + waitForWrapperReady(); + // Init a second time just to ensure the wrapper grabs the promise value + mWrapper->init(); + + EXPECT_EQ(mWrapper->alive(), true); + + // Schedule delayed destruction, allow it to run, and check when it's done + scheduleDelayedDestroyManaged(); + allowDelayedDestructionToStart(); + waitForDelayedDestructionToFinish(); + + // Ensure it closed within the timeframe of the test + Mock::VerifyAndClearExpectations(sMockBinding.get()); + EXPECT_EQ(mWrapper->alive(), false); + // If we then delete the wrapper, it shouldn't close the session again + EXPECT_CALL(*sMockBinding, fakeCloseSession(_)).Times(0); + mWrapper = nullptr; +} + +TEST_F(HintSessionWrapperTests, delayedDeletionResolvesBeforeAsyncCreationFinishes) { + // Here we test whether queueing delayedDestroy works while creation is still happening, if + // creation happens after + EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); + sMockBinding->createSession = &stubManagedCreateSession; + + // Start creating the session and destroying it at the same time + mWrapper->init(); + scheduleDelayedDestroyManaged(); + + // Allow destruction to happen first + allowDelayedDestructionToStart(); + + // Make sure destruction has had time to happen + std::this_thread::sleep_for(50ms); + + // Then, allow creation to finish after delayed destroy runs + allowCreationToFinish(); + + // Wait for destruction to finish + waitForDelayedDestructionToFinish(); + + // Ensure it closed within the timeframe of the test + Mock::VerifyAndClearExpectations(sMockBinding.get()); + EXPECT_EQ(mWrapper->alive(), false); +} + +TEST_F(HintSessionWrapperTests, delayedDeletionResolvesAfterAsyncCreationFinishes) { + // Here we test whether queueing delayedDestroy works while creation is still happening, if + // creation happens before + EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); + sMockBinding->createSession = &stubManagedCreateSession; + + // Start creating the session and destroying it at the same time + mWrapper->init(); + scheduleDelayedDestroyManaged(); + + // Allow creation to happen first + allowCreationToFinish(); + + // Make sure creation has had time to happen + waitForWrapperReady(); + + // Then allow destruction to happen after creation is done + allowDelayedDestructionToStart(); + + // Wait for it to finish + waitForDelayedDestructionToFinish(); + + // Ensure it closed within the timeframe of the test + Mock::VerifyAndClearExpectations(sMockBinding.get()); + EXPECT_EQ(mWrapper->alive(), false); +} + +TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) { + EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0); + EXPECT_CALL(*sMockBinding, fakeReportActualWorkDuration(sessionPtr, 5_ms)).Times(1); + + mWrapper->init(); + waitForWrapperReady(); + // Init a second time just to grab the wrapper from the promise + mWrapper->init(); + EXPECT_EQ(mWrapper->alive(), true); + + // First schedule the deletion + scheduleDelayedDestroyManaged(); + + // Then, report an actual duration + mWrapper->reportActualWorkDuration(5_ms); + + // Then, run the delayed deletion after sending the update + allowDelayedDestructionToStart(); + waitForDelayedDestructionToFinish(); + + // Ensure it didn't close within the timeframe of the test + Mock::VerifyAndClearExpectations(sMockBinding.get()); + EXPECT_EQ(mWrapper->alive(), true); +} + +TEST_F(HintSessionWrapperTests, loadUpDoesNotResetDeletionTimer) { + EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); + EXPECT_CALL(*sMockBinding, + fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP))) + .Times(1); + + mWrapper->init(); + waitForWrapperReady(); + // Init a second time just to grab the wrapper from the promise + mWrapper->init(); + EXPECT_EQ(mWrapper->alive(), true); + + // First schedule the deletion + scheduleDelayedDestroyManaged(); + + // Then, send a load_up hint + mWrapper->sendLoadIncreaseHint(); + + // Then, run the delayed deletion after sending the update + allowDelayedDestructionToStart(); + waitForDelayedDestructionToFinish(); + + // Ensure it closed within the timeframe of the test + Mock::VerifyAndClearExpectations(sMockBinding.get()); + EXPECT_EQ(mWrapper->alive(), false); +} + +TEST_F(HintSessionWrapperTests, manualSessionDestroyPlaysNiceWithDelayedDestruct) { + EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1); + + mWrapper->init(); + waitForWrapperReady(); + // Init a second time just to grab the wrapper from the promise + mWrapper->init(); + EXPECT_EQ(mWrapper->alive(), true); + + // First schedule the deletion + scheduleDelayedDestroyManaged(); + + // Then, kill the session + mWrapper->destroy(); + + // Verify it died + Mock::VerifyAndClearExpectations(sMockBinding.get()); + EXPECT_EQ(mWrapper->alive(), false); + + EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0); + + // Then, run the delayed deletion after manually killing the session + allowDelayedDestructionToStart(); + waitForDelayedDestructionToFinish(); + + // Ensure it didn't close again and is still dead + Mock::VerifyAndClearExpectations(sMockBinding.get()); + EXPECT_EQ(mWrapper->alive(), false); +} + } // namespace android::uirenderer::renderthread
\ No newline at end of file diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index 596bd37e4cf5..073a8357e574 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -14,20 +14,22 @@ * limitations under the License. */ -#include <VectorDrawable.h> -#include <gtest/gtest.h> - #include <SkBlendMode.h> #include <SkClipStack.h> #include <SkSurface_Base.h> +#include <VectorDrawable.h> +#include <gtest/gtest.h> +#include <include/effects/SkImageFilters.h> #include <string.h> + #include "AnimationContext.h" #include "DamageAccumulator.h" #include "FatalTestCanvas.h" #include "IContextFactory.h" -#include "hwui/Paint.h" #include "RecordingCanvas.h" #include "SkiaCanvas.h" +#include "hwui/Paint.h" +#include "pipeline/skia/BackdropFilterDrawable.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaPipeline.h" @@ -141,7 +143,7 @@ TEST(RenderNodeDrawable, zReorder) { } TEST(RenderNodeDrawable, composeOnLayer) { - auto surface = SkSurface::MakeRasterN32Premul(1, 1); + auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1)); SkCanvas& canvas = *surface->getCanvas(); canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); @@ -152,7 +154,7 @@ TEST(RenderNodeDrawable, composeOnLayer) { }); // attach a layer to the render node - auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1); + auto surfaceLayer = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1)); auto canvas2 = surfaceLayer->getCanvas(); canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); rootNode->setLayerSurface(surfaceLayer); @@ -187,7 +189,7 @@ static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) { } TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) { - auto surface = SkSurface::MakeRasterN32Premul(400, 800); + auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(400, 800)); SkCanvas& canvas = *surface->getCanvas(); canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE); @@ -353,7 +355,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { EXPECT_EQ(3, canvas.getIndex()); } -RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { +RENDERTHREAD_TEST(RenderNodeDrawable, emptyReceiver) { class ProjectionTestCanvas : public SkCanvas { public: ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {} @@ -417,7 +419,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { EXPECT_EQ(2, canvas.getDrawCounter()); } -RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { +RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) { /* R is backward projected on B and C is a layer. A / \ @@ -1050,7 +1052,7 @@ TEST(RenderNodeDrawable, renderNode) { } // Verify that layers are composed with linear filtering. -RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) { +RENDERTHREAD_TEST(RenderNodeDrawable, layerComposeQuality) { static const int CANVAS_WIDTH = 1; static const int CANVAS_HEIGHT = 1; static const int LAYER_WIDTH = 1; @@ -1074,7 +1076,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) { }); layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer); - layerNode->setLayerSurface(SkSurface::MakeRasterN32Premul(LAYER_WIDTH, LAYER_HEIGHT)); + layerNode->setLayerSurface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(LAYER_WIDTH, + LAYER_HEIGHT))); FrameTestCanvas canvas; RenderNodeDrawable drawable(layerNode.get(), &canvas, true); @@ -1167,7 +1170,7 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { } // Draw a vector drawable twice but with different bounds and verify correct bounds are used. -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) { +RENDERTHREAD_TEST(SkiaRecordingCanvas, drawVectorDrawable) { static const int CANVAS_WIDTH = 100; static const int CANVAS_HEIGHT = 200; class VectorDrawableTestCanvas : public TestCanvasBase { @@ -1210,3 +1213,77 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) { canvas.drawDrawable(&drawable); EXPECT_EQ(2, canvas.mDrawCounter); } + +// Verify drawing logics for BackdropFilterDrawable +RENDERTHREAD_TEST(BackdropFilterDrawable, drawing) { + static const int CANVAS_WIDTH = 100; + static const int CANVAS_HEIGHT = 200; + class SimpleTestCanvas : public TestCanvasBase { + public: + SkRect mDstBounds; + SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {} + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { + // did nothing. + } + + // called when BackdropFilterDrawable is drawn. + void onDrawImageRect2(const SkImage*, const SkRect& src, const SkRect& dst, + const SkSamplingOptions&, const SkPaint*, + SrcRectConstraint) override { + mDrawCounter++; + mDstBounds = dst; + } + }; + class SimpleLayer : public SkSurface_Base { + public: + SimpleLayer() + : SkSurface_Base(SkImageInfo::MakeN32Premul(CANVAS_WIDTH, CANVAS_HEIGHT), nullptr) { + } + virtual sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override { + SkBitmap bitmap; + bitmap.allocN32Pixels(CANVAS_WIDTH, CANVAS_HEIGHT); + bitmap.setImmutable(); + return bitmap.asImage(); + } + SkCanvas* onNewCanvas() override { return new SimpleTestCanvas(); } + sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; } + bool onCopyOnWrite(ContentChangeMode) override { return true; } + void onWritePixels(const SkPixmap&, int x, int y) {} + }; + + auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + Paint()); + }); + + sk_sp<SkSurface> surface(new SimpleLayer()); + auto* canvas = reinterpret_cast<SimpleTestCanvas*>(surface->getCanvas()); + RenderNodeDrawable drawable(node.get(), canvas, true); + BackdropFilterDrawable backdropDrawable(node.get(), canvas); + canvas->drawDrawable(&drawable); + canvas->drawDrawable(&backdropDrawable); + // no backdrop filter, skip drawing. + EXPECT_EQ(0, canvas->mDrawCounter); + + sk_sp<SkImageFilter> filter(SkImageFilters::Blur(3, 3, nullptr)); + node->animatorProperties().mutateLayerProperties().setBackdropImageFilter(filter.get()); + canvas->drawDrawable(&drawable); + canvas->drawDrawable(&backdropDrawable); + // backdrop filter is set, ok to draw. + EXPECT_EQ(1, canvas->mDrawCounter); + EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), canvas->mDstBounds); + + canvas->translate(30, 30); + canvas->drawDrawable(&drawable); + canvas->drawDrawable(&backdropDrawable); + // the drawable is still visible, ok to draw. + EXPECT_EQ(2, canvas->mDrawCounter); + EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH - 30, CANVAS_HEIGHT - 30), canvas->mDstBounds); + + canvas->translate(CANVAS_WIDTH, CANVAS_HEIGHT); + canvas->drawDrawable(&drawable); + canvas->drawDrawable(&backdropDrawable); + // the drawable is invisible, skip drawing. + EXPECT_EQ(2, canvas->mDrawCounter); +} diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index 80796f4a4111..8273524167f9 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -231,8 +231,7 @@ TEST(RenderNode, multiTreeValidity) { } TEST(RenderNode, releasedCallback) { - int functor = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + int functor = TestUtils::createMockFunctor(); auto node = TestUtils::createNode(0, 0, 200, 400, [&](RenderProperties& props, Canvas& canvas) { canvas.drawWebViewFunctor(functor); diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp index 9aa2e1db4461..0f8bd1368f5a 100644 --- a/libs/hwui/tests/unit/ShaderCacheTests.cpp +++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp @@ -370,9 +370,9 @@ TEST(ShaderCacheTest, testCacheValidation) { } using namespace android::uirenderer; -RENDERTHREAD_SKIA_PIPELINE_TEST(ShaderCacheTest, testOnVkFrameFlushed) { +RENDERTHREAD_TEST(ShaderCacheTest, testOnVkFrameFlushed) { if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) { - // RENDERTHREAD_SKIA_PIPELINE_TEST declares both SkiaVK and SkiaGL variants. + // RENDERTHREAD_TEST declares both SkiaVK and SkiaGL variants. GTEST_SKIP() << "This test is only applicable to RenderPipelineType::SkiaVulkan"; } if (!folderExist(getExternalStorageFolder())) { diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index 87c52161d68e..e53fcaa474e2 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -36,7 +36,7 @@ using namespace android; using namespace android::uirenderer; TEST(SkiaCanvas, drawShadowLayer) { - auto surface = SkSurface::MakeRasterN32Premul(10, 10); + auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(10, 10)); SkiaCanvas canvas(surface->getCanvas()); // clear to white diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index f825d7c5d9cc..064d42ec8941 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -48,8 +48,7 @@ TEST(SkiaDisplayList, reset) { SkCanvas dummyCanvas; RenderNodeDrawable drawable(nullptr, &dummyCanvas); skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas); - int functor1 = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + int functor1 = TestUtils::createMockFunctor(); GLFunctorDrawable functorDrawable{functor1, &dummyCanvas}; WebViewFunctor_release(functor1); skiaDL->mChildFunctors.push_back(&functorDrawable); @@ -101,8 +100,7 @@ TEST(SkiaDisplayList, syncContexts) { SkCanvas dummyCanvas; - int functor1 = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + int functor1 = TestUtils::createMockFunctor(); auto& counts = TestUtils::countsForFunctor(functor1); skiaDL.mChildFunctors.push_back( skiaDL.allocateDrawable<GLFunctorDrawable>(functor1, &dummyCanvas)); @@ -131,6 +129,33 @@ TEST(SkiaDisplayList, syncContexts) { EXPECT_EQ(counts.destroyed, 1); } +TEST(SkiaDisplayList, recordMutableBitmap) { + SkiaRecordingCanvas canvas{nullptr, 100, 100}; + auto bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::Make( + 10, 20, SkColorType::kN32_SkColorType, SkAlphaType::kPremul_SkAlphaType)); + EXPECT_FALSE(bitmap->isImmutable()); + canvas.drawBitmap(*bitmap, 0, 0, nullptr); + auto displayList = canvas.finishRecording(); + ASSERT_EQ(1, displayList->mMutableImages.size()); + EXPECT_EQ(10, displayList->mMutableImages[0]->width()); + EXPECT_EQ(20, displayList->mMutableImages[0]->height()); +} + +TEST(SkiaDisplayList, recordMutableBitmapInShader) { + SkiaRecordingCanvas canvas{nullptr, 100, 100}; + auto bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::Make( + 10, 20, SkColorType::kN32_SkColorType, SkAlphaType::kPremul_SkAlphaType)); + EXPECT_FALSE(bitmap->isImmutable()); + SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNone); + Paint paint; + paint.setShader(bitmap->makeImage()->makeShader(sampling)); + canvas.drawPaint(paint); + auto displayList = canvas.finishRecording(); + ASSERT_EQ(1, displayList->mMutableImages.size()); + EXPECT_EQ(10, displayList->mMutableImages[0]->width()); + EXPECT_EQ(20, displayList->mMutableImages[0]->height()); +} + class ContextFactory : public IContextFactory { public: virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { @@ -138,7 +163,7 @@ public: } }; -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { +RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren) { auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( @@ -197,7 +222,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { canvasContext->destroy(); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) { +RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) { auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 4d0595e03da6..3ded540c3152 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -42,7 +42,7 @@ using namespace android::uirenderer; using namespace android::uirenderer::renderthread; using namespace android::uirenderer::skiapipeline; -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { +RENDERTHREAD_TEST(SkiaPipeline, renderFrame) { auto redNode = TestUtils::createSkiaNode( 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); @@ -54,7 +54,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { bool opaque = true; android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); - auto surface = SkSurface::MakeRasterN32Premul(1, 1); + auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1)); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, @@ -62,7 +62,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { +RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckOpaque) { auto halfGreenNode = TestUtils::createSkiaNode( 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) { Paint greenPaint; @@ -76,7 +76,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { renderNodes.push_back(halfGreenNode); android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); - auto surface = SkSurface::MakeRasterN32Premul(2, 2); + auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(2, 2)); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface, @@ -89,7 +89,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { +RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { auto redNode = TestUtils::createSkiaNode( 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); @@ -100,7 +100,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { renderNodes.push_back(redNode); android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); - auto surface = SkSurface::MakeRasterN32Premul(2, 2); + auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(2, 2)); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface, @@ -111,12 +111,12 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { +RENDERTHREAD_TEST(SkiaPipeline, renderLayer) { auto redNode = TestUtils::createSkiaNode( 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); }); - auto surfaceLayer1 = SkSurface::MakeRasterN32Premul(1, 1); + auto surfaceLayer1 = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1)); surfaceLayer1->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorWHITE); redNode->setLayerSurface(surfaceLayer1); @@ -127,7 +127,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& blueCanvas) { blueCanvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); }); - auto surfaceLayer2 = SkSurface::MakeRasterN32Premul(2, 2); + auto surfaceLayer2 = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(2, 2)); surfaceLayer2->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorWHITE); blueNode->setLayerSurface(surfaceLayer2); @@ -154,7 +154,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { blueNode->setLayerSurface(sk_sp<SkSurface>()); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) { +RENDERTHREAD_TEST(SkiaPipeline, renderOverdraw) { ScopedProperty<bool> prop(Properties::debugOverdraw, true); auto whiteNode = TestUtils::createSkiaNode( @@ -169,7 +169,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) { // empty contentDrawBounds is avoiding backdrop/content logic, which would lead to less overdraw android::uirenderer::Rect contentDrawBounds(0, 0, 0, 0); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); - auto surface = SkSurface::MakeRasterN32Premul(1, 1); + auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1)); // Initialize the canvas to blue. surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); @@ -227,7 +227,7 @@ public: }; } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { +RENDERTHREAD_TEST(SkiaPipeline, deferRenderNodeScene) { class DeferTestCanvas : public SkCanvas { public: DeferTestCanvas() : SkCanvas(800, 600) {} @@ -297,7 +297,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { EXPECT_EQ(4, surface->canvas()->mDrawCounter); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) { +RENDERTHREAD_TEST(SkiaPipeline, clipped) { static const int CANVAS_WIDTH = 200; static const int CANVAS_HEIGHT = 200; class ClippedTestCanvas : public SkCanvas { @@ -330,7 +330,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) { } // Test renderFrame with a dirty clip and a pre-transform matrix. -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) { +RENDERTHREAD_TEST(SkiaPipeline, clipped_rotated) { static const int CANVAS_WIDTH = 200; static const int CANVAS_HEIGHT = 100; static const SkMatrix rotateMatrix = SkMatrix::MakeAll(0, -1, CANVAS_HEIGHT, 1, 0, 0, 0, 0, 1); @@ -366,7 +366,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) { EXPECT_EQ(1, surface->canvas()->mDrawCounter); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { +RENDERTHREAD_TEST(SkiaPipeline, clip_replace) { static const int CANVAS_WIDTH = 50; static const int CANVAS_HEIGHT = 50; class ClipReplaceTestCanvas : public SkCanvas { @@ -396,7 +396,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { EXPECT_EQ(1, surface->canvas()->mDrawCounter); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { +RENDERTHREAD_TEST(SkiaPipeline, context_lost) { test::TestContext context; auto surface = context.surface(); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); @@ -410,7 +410,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { EXPECT_TRUE(pipeline->isSurfaceReady()); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) { +RENDERTHREAD_TEST(SkiaPipeline, pictureCallback) { // create a pipeline and add a picture callback auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); int callbackCount = 0; @@ -428,7 +428,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) { renderNodes.push_back(redNode); bool opaque = true; android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); - auto surface = SkSurface::MakeRasterN32Premul(1, 1); + auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(1, 1)); pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, SkMatrix::I()); diff --git a/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp new file mode 100644 index 000000000000..db2be20936fb --- /dev/null +++ b/libs/hwui/tests/unit/UnderlineTest.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fcntl.h> +#include <flag_macros.h> +#include <gtest/gtest.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <utils/Log.h> + +#include "SkAlphaType.h" +#include "SkBitmap.h" +#include "SkData.h" +#include "SkFontMgr.h" +#include "SkImageInfo.h" +#include "SkRefCnt.h" +#include "SkStream.h" +#include "SkTypeface.h" +#include "SkiaCanvas.h" +#include "hwui/Bitmap.h" +#include "hwui/DrawTextFunctor.h" +#include "hwui/MinikinSkia.h" +#include "hwui/MinikinUtils.h" +#include "hwui/Paint.h" +#include "hwui/Typeface.h" + +using namespace android; + +namespace { + +constexpr char kRobotoVariable[] = "/system/fonts/Roboto-Regular.ttf"; +constexpr char kJPFont[] = "/system/fonts/NotoSansCJK-Regular.ttc"; + +// The underline position and thickness are cames from post table. +constexpr float ROBOTO_POSITION_EM = 150.0 / 2048.0; +constexpr float ROBOTO_THICKNESS_EM = 100.0 / 2048.0; +constexpr float NOTO_CJK_POSITION_EM = 125.0 / 1000.0; +constexpr float NOTO_CJK_THICKNESS_EM = 50.0 / 1000.0; + +void unmap(const void* ptr, void* context) { + void* p = const_cast<void*>(ptr); + size_t len = reinterpret_cast<size_t>(context); + munmap(p, len); +} + +// Create a font family from a single font file. +std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) { + int fd = open(fileName, O_RDONLY); + LOG_ALWAYS_FATAL_IF(fd == -1, "Failed to open file %s", fileName); + struct stat st = {}; + LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", fileName); + void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + sk_sp<SkData> skData = + SkData::MakeWithProc(data, st.st_size, unmap, reinterpret_cast<void*>(st.st_size)); + std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(skData)); + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData))); + LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName); + std::shared_ptr<minikin::MinikinFont> font = + std::make_shared<MinikinFontSkia>(std::move(typeface), 0, data, st.st_size, fileName, 0, + std::vector<minikin::FontVariation>()); + std::vector<std::shared_ptr<minikin::Font>> fonts; + fonts.push_back(minikin::Font::Builder(font).build()); + return minikin::FontFamily::create(std::move(fonts)); +} + +// Create a typeface from roboto and NotoCJK. +Typeface* makeTypeface() { + return Typeface::createFromFamilies( + std::vector<std::shared_ptr<minikin::FontFamily>>( + {buildFamily(kRobotoVariable), buildFamily(kJPFont)}), + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE, nullptr /* fallback */); +} + +// Execute a text layout. +minikin::Layout doLayout(const std::vector<uint16_t> text, Paint paint, Typeface* typeface) { + return MinikinUtils::doLayout(&paint, minikin::Bidi::LTR, typeface, text.data(), text.size(), + 0 /* start */, text.size(), 0, text.size(), nullptr); +} + +DrawTextFunctor processFunctor(const std::vector<uint16_t>& text, Paint* paint) { + // Create canvas + SkImageInfo info = SkImageInfo::Make(1, 1, kN32_SkColorType, kOpaque_SkAlphaType); + sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + SkiaCanvas canvas(skBitmap); + + // Create minikin::Layout + std::unique_ptr<Typeface> typeface(makeTypeface()); + minikin::Layout layout = doLayout(text, *paint, typeface.get()); + + DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance()); + MinikinUtils::forFontRun(layout, paint, f); + return f; +} + +TEST_WITH_FLAGS(UnderlineTest, Roboto, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags, + fix_double_underline))) { + float textSize = 100; + Paint paint; + paint.getSkFont().setSize(textSize); + paint.setUnderline(true); + // the text is "abc" + DrawTextFunctor functor = processFunctor({0x0061, 0x0062, 0x0063}, &paint); + + EXPECT_EQ(ROBOTO_POSITION_EM * textSize, functor.getUnderlinePosition()); + EXPECT_EQ(ROBOTO_THICKNESS_EM * textSize, functor.getUnderlineThickness()); +} + +TEST_WITH_FLAGS(UnderlineTest, NotoCJK, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags, + fix_double_underline))) { + float textSize = 100; + Paint paint; + paint.getSkFont().setSize(textSize); + paint.setUnderline(true); + // The text is ććć in Japanese + DrawTextFunctor functor = processFunctor({0x3042, 0x3044, 0x3046}, &paint); + + EXPECT_EQ(NOTO_CJK_POSITION_EM * textSize, functor.getUnderlinePosition()); + EXPECT_EQ(NOTO_CJK_THICKNESS_EM * textSize, functor.getUnderlineThickness()); +} + +TEST_WITH_FLAGS(UnderlineTest, Mixture, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags, + fix_double_underline))) { + float textSize = 100; + Paint paint; + paint.getSkFont().setSize(textSize); + paint.setUnderline(true); + // The text is aćc. The only middle of the character is Japanese. + DrawTextFunctor functor = processFunctor({0x0061, 0x3044, 0x0063}, &paint); + + // We use the bottom, thicker line as underline. Here, use Noto's one. + EXPECT_EQ(NOTO_CJK_POSITION_EM * textSize, functor.getUnderlinePosition()); + EXPECT_EQ(NOTO_CJK_THICKNESS_EM * textSize, functor.getUnderlineThickness()); +} +} // namespace diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp index e1fb8b7069ff..5e8f13d261c7 100644 --- a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp +++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp @@ -26,9 +26,15 @@ using namespace android; using namespace android::uirenderer; +#define ASSUME_GLES() \ + if (WebViewFunctor_queryPlatformRenderMode() != RenderMode::OpenGL_ES) \ + GTEST_SKIP() << "Not in GLES, skipping test" + TEST(WebViewFunctor, createDestroyGLES) { + ASSUME_GLES(); int functor = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); ASSERT_NE(-1, functor); WebViewFunctor_release(functor); TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) { @@ -41,8 +47,10 @@ TEST(WebViewFunctor, createDestroyGLES) { } TEST(WebViewFunctor, createSyncHandleGLES) { + ASSUME_GLES(); int functor = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); ASSERT_NE(-1, functor); auto handle = WebViewFunctorManager::instance().handleFor(functor); ASSERT_TRUE(handle); @@ -82,8 +90,10 @@ TEST(WebViewFunctor, createSyncHandleGLES) { } TEST(WebViewFunctor, createSyncDrawGLES) { + ASSUME_GLES(); int functor = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); ASSERT_NE(-1, functor); auto handle = WebViewFunctorManager::instance().handleFor(functor); ASSERT_TRUE(handle); @@ -108,9 +118,11 @@ TEST(WebViewFunctor, createSyncDrawGLES) { EXPECT_EQ(1, counts.destroyed); } -TEST(WebViewFunctor, contextDestroyed) { +TEST(WebViewFunctor, contextDestroyedGLES) { + ASSUME_GLES(); int functor = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); ASSERT_NE(-1, functor); auto handle = WebViewFunctorManager::instance().handleFor(functor); ASSERT_TRUE(handle); diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 10c874ec3f12..76cbc8abc808 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -14,15 +14,15 @@ * limitations under the License. */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" +#include <getopt.h> +#include <signal.h> #include "Properties.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include "hwui/Typeface.h" #include "tests/common/LeakChecker.h" -#include <signal.h> - using namespace std; using namespace android; using namespace android::uirenderer; @@ -45,6 +45,57 @@ static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) { raise(sig); } +// For options that only exist in long-form. Anything in the +// 0-255 range is reserved for short options (which just use their ASCII value) +namespace LongOpts { +enum { + Reserved = 255, + Renderer, +}; +} + +static const struct option LONG_OPTIONS[] = { + {"renderer", required_argument, nullptr, LongOpts::Renderer}, {0, 0, 0, 0}}; + +static RenderPipelineType parseRenderer(const char* renderer) { + // Anything that's not skiavk is skiagl + if (!strcmp(renderer, "skiavk")) { + return RenderPipelineType::SkiaVulkan; + } + return RenderPipelineType::SkiaGL; +} + +struct Options { + RenderPipelineType renderer = RenderPipelineType::SkiaGL; +}; + +Options parseOptions(int argc, char* argv[]) { + int c; + opterr = 0; + Options opts; + + while (true) { + /* getopt_long stores the option index here. */ + int option_index = 0; + + c = getopt_long(argc, argv, "", LONG_OPTIONS, &option_index); + + if (c == -1) break; + + switch (c) { + case 0: + // Option set a flag, don't need to do anything + // (although none of the current LONG_OPTIONS do this...) + break; + + case LongOpts::Renderer: + opts.renderer = parseRenderer(optarg); + break; + } + } + return opts; +} + class TypefaceEnvironment : public testing::Environment { public: virtual void SetUp() { Typeface::setRobotoTypefaceForTest(); } @@ -64,8 +115,9 @@ int main(int argc, char* argv[]) { // Avoid talking to SF Properties::isolatedProcess = true; - // Default to GLES (Vulkan-aware tests will override this) - Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL); + + auto opts = parseOptions(argc, argv); + Properties::overrideRenderPipelineType(opts.renderer); // Run the tests testing::InitGoogleTest(&argc, argv); diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 6c0fd5f65359..5ce990fdeb82 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -23,6 +23,12 @@ package { cc_library_shared { name: "libinputservice", + defaults: [ + // Build using the same flags and configurations as inputflinger. + "inputflinger_defaults", + ], + host_supported: false, + srcs: [ "PointerController.cpp", "PointerControllerContext.cpp", @@ -50,12 +56,4 @@ cc_library_shared { ], include_dirs: ["frameworks/native/services"], - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - "-Wthread-safety", - ], - } diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp index c3ad7670d473..6a465442c2b4 100644 --- a/libs/input/MouseCursorController.cpp +++ b/libs/input/MouseCursorController.cpp @@ -47,7 +47,7 @@ MouseCursorController::MouseCursorController(PointerControllerContext& context) mLocked.pointerX = 0; mLocked.pointerY = 0; mLocked.pointerAlpha = 0.0f; // pointer is initially faded - mLocked.pointerSprite = mContext.getSpriteController()->createSprite(); + mLocked.pointerSprite = mContext.getSpriteController().createSprite(); mLocked.updatePointerIcon = false; mLocked.requestedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED; mLocked.resolvedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED; @@ -325,8 +325,8 @@ bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES( } if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) { - sp<SpriteController> spriteController = mContext.getSpriteController(); - spriteController->openTransaction(); + auto& spriteController = mContext.getSpriteController(); + spriteController.openTransaction(); int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame; mLocked.animationFrameIndex += incr; @@ -336,7 +336,7 @@ bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES( } mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]); - spriteController->closeTransaction(); + spriteController.closeTransaction(); } // Keep animating. return true; @@ -346,8 +346,8 @@ void MouseCursorController::updatePointerLocked() REQUIRES(mLock) { if (!mLocked.viewport.isValid()) { return; } - sp<SpriteController> spriteController = mContext.getSpriteController(); - spriteController->openTransaction(); + auto& spriteController = mContext.getSpriteController(); + spriteController.openTransaction(); mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); @@ -392,7 +392,7 @@ void MouseCursorController::updatePointerLocked() REQUIRES(mLock) { mLocked.updatePointerIcon = false; } - spriteController->closeTransaction(); + spriteController.closeTransaction(); } void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) { diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index e21d6fb2fe14..abd928486607 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -63,10 +63,10 @@ void PointerController::DisplayInfoListener::onPointerControllerDestroyed() { std::shared_ptr<PointerController> PointerController::create( const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, - const sp<SpriteController>& spriteController) { + SpriteController& spriteController, bool enabled) { // using 'new' to access non-public constructor std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>( - new PointerController(policy, looper, spriteController)); + new PointerController(policy, looper, spriteController, enabled)); /* * Now we need to hook up the constructed PointerController object to its callbacks. @@ -85,10 +85,10 @@ std::shared_ptr<PointerController> PointerController::create( } PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, - const sp<Looper>& looper, - const sp<SpriteController>& spriteController) + const sp<Looper>& looper, SpriteController& spriteController, + bool enabled) : PointerController( - policy, looper, spriteController, + policy, looper, spriteController, enabled, [](const sp<android::gui::WindowInfosListener>& listener) { SurfaceComposerClient::getDefault()->addWindowInfosListener(listener); }, @@ -97,13 +97,13 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>& }) {} PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, - const sp<Looper>& looper, - const sp<SpriteController>& spriteController, - WindowListenerConsumer registerListener, + const sp<Looper>& looper, SpriteController& spriteController, + bool enabled, WindowListenerConsumer registerListener, WindowListenerConsumer unregisterListener) - : mContext(policy, looper, spriteController, *this), + : mEnabled(enabled), + mContext(policy, looper, spriteController, *this), mCursorController(mContext), - mDisplayInfoListener(new DisplayInfoListener(this)), + mDisplayInfoListener(sp<DisplayInfoListener>::make(this)), mUnregisterWindowInfosListener(std::move(unregisterListener)) { std::scoped_lock lock(getLock()); mLocked.presentation = Presentation::SPOT; @@ -121,10 +121,14 @@ std::mutex& PointerController::getLock() const { } std::optional<FloatRect> PointerController::getBounds() const { + if (!mEnabled) return {}; + return mCursorController.getBounds(); } void PointerController::move(float deltaX, float deltaY) { + if (!mEnabled) return; + const int32_t displayId = mCursorController.getDisplayId(); vec2 transformed; { @@ -136,6 +140,8 @@ void PointerController::move(float deltaX, float deltaY) { } void PointerController::setPosition(float x, float y) { + if (!mEnabled) return; + const int32_t displayId = mCursorController.getDisplayId(); vec2 transformed; { @@ -147,6 +153,11 @@ void PointerController::setPosition(float x, float y) { } FloatPoint PointerController::getPosition() const { + if (!mEnabled) { + return FloatPoint{AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION}; + } + const int32_t displayId = mCursorController.getDisplayId(); const auto p = mCursorController.getPosition(); { @@ -157,20 +168,28 @@ FloatPoint PointerController::getPosition() const { } int32_t PointerController::getDisplayId() const { + if (!mEnabled) return ADISPLAY_ID_NONE; + return mCursorController.getDisplayId(); } void PointerController::fade(Transition transition) { + if (!mEnabled) return; + std::scoped_lock lock(getLock()); mCursorController.fade(transition); } void PointerController::unfade(Transition transition) { + if (!mEnabled) return; + std::scoped_lock lock(getLock()); mCursorController.unfade(transition); } void PointerController::setPresentation(Presentation presentation) { + if (!mEnabled) return; + std::scoped_lock lock(getLock()); if (mLocked.presentation == presentation) { @@ -195,6 +214,8 @@ void PointerController::setPresentation(Presentation presentation) { void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) { + if (!mEnabled) return; + std::scoped_lock lock(getLock()); std::array<PointerCoords, MAX_POINTERS> outSpotCoords{}; const ui::Transform& transform = getTransformForDisplayLocked(displayId); @@ -218,6 +239,8 @@ void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t } void PointerController::clearSpots() { + if (!mEnabled) return; + std::scoped_lock lock(getLock()); clearSpotsLocked(); } @@ -279,11 +302,15 @@ void PointerController::setDisplayViewport(const DisplayViewport& viewport) { } void PointerController::updatePointerIcon(PointerIconStyle iconId) { + if (!mEnabled) return; + std::scoped_lock lock(getLock()); mCursorController.updatePointerIcon(iconId); } void PointerController::setCustomPointerIcon(const SpriteIcon& icon) { + if (!mEnabled) return; + std::scoped_lock lock(getLock()); mCursorController.setCustomPointerIcon(icon); } @@ -292,7 +319,7 @@ void PointerController::doInactivityTimeout() { fade(Transition::GRADUAL); } -void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) { +void PointerController::onDisplayViewportsUpdated(const std::vector<DisplayViewport>& viewports) { std::unordered_set<int32_t> displayIdSet; for (const DisplayViewport& viewport : viewports) { displayIdSet.insert(viewport.displayId); @@ -327,8 +354,12 @@ const ui::Transform& PointerController::getTransformForDisplayLocked(int display return it != di.end() ? it->transform : kIdentityTransform; } -void PointerController::dump(std::string& dump) { - dump += INDENT "PointerController:\n"; +std::string PointerController::dump() { + if (!mEnabled) { + return INDENT "PointerController: DISABLED due to ongoing PointerChoreographer refactor\n"; + } + + std::string dump = INDENT "PointerController:\n"; std::scoped_lock lock(getLock()); dump += StringPrintf(INDENT2 "Presentation: %s\n", ftl::enum_string(mLocked.presentation).c_str()); @@ -341,6 +372,7 @@ void PointerController::dump(std::string& dump) { for (const auto& [_, spotController] : mLocked.spotControllers) { spotController.dump(dump, INDENT3); } + return dump; } } // namespace android diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 62ee74331302..aa7ca3c52ecf 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -47,7 +47,7 @@ class PointerController : public PointerControllerInterface { public: static std::shared_ptr<PointerController> create( const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, - const sp<SpriteController>& spriteController); + SpriteController& spriteController, bool enabled); ~PointerController() override; @@ -70,12 +70,12 @@ public: void setInactivityTimeout(InactivityTimeout inactivityTimeout); void doInactivityTimeout(); void reloadPointerResources(); - void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports); + void onDisplayViewportsUpdated(const std::vector<DisplayViewport>& viewports); void onDisplayInfosChangedLocked(const std::vector<gui::DisplayInfo>& displayInfos) REQUIRES(getLock()); - void dump(std::string& dump); + std::string dump(); protected: using WindowListenerConsumer = @@ -83,13 +83,13 @@ protected: // Constructor used to test WindowInfosListener registration. PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, - const sp<SpriteController>& spriteController, + SpriteController& spriteController, bool enabled, WindowListenerConsumer registerListener, WindowListenerConsumer unregisterListener); private: PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, - const sp<SpriteController>& spriteController); + SpriteController& spriteController, bool enabled); friend PointerControllerContext::LooperCallback; friend PointerControllerContext::MessageHandler; @@ -100,6 +100,8 @@ private: // we use the DisplayInfoListener's lock in PointerController. std::mutex& getLock() const; + const bool mEnabled; + PointerControllerContext mContext; MouseCursorController mCursorController; diff --git a/libs/input/PointerControllerContext.cpp b/libs/input/PointerControllerContext.cpp index f30e8d8e33a5..15c35176afce 100644 --- a/libs/input/PointerControllerContext.cpp +++ b/libs/input/PointerControllerContext.cpp @@ -32,12 +32,12 @@ namespace android { PointerControllerContext::PointerControllerContext( const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, - const sp<SpriteController>& spriteController, PointerController& controller) + SpriteController& spriteController, PointerController& controller) : mPolicy(policy), mLooper(looper), mSpriteController(spriteController), - mHandler(new MessageHandler()), - mCallback(new LooperCallback()), + mHandler(sp<MessageHandler>::make()), + mCallback(sp<LooperCallback>::make()), mController(controller), mAnimator(*this) { std::scoped_lock lock(mLock); @@ -93,7 +93,7 @@ sp<PointerControllerPolicyInterface> PointerControllerContext::getPolicy() { return mPolicy; } -sp<SpriteController> PointerControllerContext::getSpriteController() { +SpriteController& PointerControllerContext::getSpriteController() { return mSpriteController; } diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h index f6f5d3bc51bd..98c3988e7df4 100644 --- a/libs/input/PointerControllerContext.h +++ b/libs/input/PointerControllerContext.h @@ -92,7 +92,7 @@ public: class PointerControllerContext { public: PointerControllerContext(const sp<PointerControllerPolicyInterface>& policy, - const sp<Looper>& looper, const sp<SpriteController>& spriteController, + const sp<Looper>& looper, SpriteController& spriteController, PointerController& controller); ~PointerControllerContext(); @@ -109,7 +109,7 @@ public: void setCallbackController(std::shared_ptr<PointerController> controller); sp<PointerControllerPolicyInterface> getPolicy(); - sp<SpriteController> getSpriteController(); + SpriteController& getSpriteController(); void handleDisplayEvents(); @@ -163,7 +163,7 @@ private: sp<PointerControllerPolicyInterface> mPolicy; sp<Looper> mLooper; - sp<SpriteController> mSpriteController; + SpriteController& mSpriteController; sp<MessageHandler> mHandler; sp<LooperCallback> mCallback; diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index 130b204954b4..6dc45a6aebec 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -31,12 +31,19 @@ SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLaye ParentSurfaceProvider parentSurfaceProvider) : mLooper(looper), mOverlayLayer(overlayLayer), + mHandler(sp<Handler>::make()), mParentSurfaceProvider(std::move(parentSurfaceProvider)) { - mHandler = new WeakMessageHandler(this); mLocked.transactionNestingCount = 0; mLocked.deferredSpriteUpdate = false; } +void SpriteController::setHandlerController( + const std::shared_ptr<android::SpriteController>& controller) { + // Initialize the weak message handler outside the constructor, because we cannot get a shared + // pointer to self in the constructor. + mHandler->spriteController = controller; +} + SpriteController::~SpriteController() { mLooper->removeMessages(mHandler); @@ -47,7 +54,7 @@ SpriteController::~SpriteController() { } sp<Sprite> SpriteController::createSprite() { - return new SpriteImpl(this); + return sp<SpriteImpl>::make(*this); } void SpriteController::openTransaction() { @@ -65,7 +72,7 @@ void SpriteController::closeTransaction() { mLocked.transactionNestingCount -= 1; if (mLocked.transactionNestingCount == 0 && mLocked.deferredSpriteUpdate) { mLocked.deferredSpriteUpdate = false; - mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES)); + mLooper->sendMessage(mHandler, Message(Handler::MSG_UPDATE_SPRITES)); } } @@ -76,7 +83,7 @@ void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) { if (mLocked.transactionNestingCount != 0) { mLocked.deferredSpriteUpdate = true; } else { - mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES)); + mLooper->sendMessage(mHandler, Message(Handler::MSG_UPDATE_SPRITES)); } } } @@ -85,18 +92,7 @@ void SpriteController::disposeSurfaceLocked(const sp<SurfaceControl>& surfaceCon bool wasEmpty = mLocked.disposedSurfaces.empty(); mLocked.disposedSurfaces.push_back(surfaceControl); if (wasEmpty) { - mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES)); - } -} - -void SpriteController::handleMessage(const Message& message) { - switch (message.what) { - case MSG_UPDATE_SPRITES: - doUpdateSprites(); - break; - case MSG_DISPOSE_SURFACES: - doDisposeSurfaces(); - break; + mLooper->sendMessage(mHandler, Message(Handler::MSG_DISPOSE_SURFACES)); } } @@ -327,7 +323,7 @@ void SpriteController::doDisposeSurfaces() { void SpriteController::ensureSurfaceComposerClient() { if (mSurfaceComposerClient == NULL) { - mSurfaceComposerClient = new SurfaceComposerClient(); + mSurfaceComposerClient = sp<SurfaceComposerClient>::make(); } } @@ -353,25 +349,41 @@ sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height return surfaceControl; } -// --- SpriteController::SpriteImpl --- +// --- SpriteController::Handler --- -SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) : - mController(controller) { +void SpriteController::Handler::handleMessage(const android::Message& message) { + auto controller = spriteController.lock(); + if (!controller) { + return; + } + + switch (message.what) { + case MSG_UPDATE_SPRITES: + controller->doUpdateSprites(); + break; + case MSG_DISPOSE_SURFACES: + controller->doDisposeSurfaces(); + break; + } } +// --- SpriteController::SpriteImpl --- + +SpriteController::SpriteImpl::SpriteImpl(SpriteController& controller) : mController(controller) {} + SpriteController::SpriteImpl::~SpriteImpl() { - AutoMutex _m(mController->mLock); + AutoMutex _m(mController.mLock); // Let the controller take care of deleting the last reference to sprite // surfaces so that we do not block the caller on an IPC here. if (mLocked.state.surfaceControl != NULL) { - mController->disposeSurfaceLocked(mLocked.state.surfaceControl); + mController.disposeSurfaceLocked(mLocked.state.surfaceControl); mLocked.state.surfaceControl.clear(); } } void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) { - AutoMutex _l(mController->mLock); + AutoMutex _l(mController.mLock); uint32_t dirty; if (icon.isValid()) { @@ -401,7 +413,7 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) { } void SpriteController::SpriteImpl::setVisible(bool visible) { - AutoMutex _l(mController->mLock); + AutoMutex _l(mController.mLock); if (mLocked.state.visible != visible) { mLocked.state.visible = visible; @@ -410,7 +422,7 @@ void SpriteController::SpriteImpl::setVisible(bool visible) { } void SpriteController::SpriteImpl::setPosition(float x, float y) { - AutoMutex _l(mController->mLock); + AutoMutex _l(mController.mLock); if (mLocked.state.positionX != x || mLocked.state.positionY != y) { mLocked.state.positionX = x; @@ -420,7 +432,7 @@ void SpriteController::SpriteImpl::setPosition(float x, float y) { } void SpriteController::SpriteImpl::setLayer(int32_t layer) { - AutoMutex _l(mController->mLock); + AutoMutex _l(mController.mLock); if (mLocked.state.layer != layer) { mLocked.state.layer = layer; @@ -429,7 +441,7 @@ void SpriteController::SpriteImpl::setLayer(int32_t layer) { } void SpriteController::SpriteImpl::setAlpha(float alpha) { - AutoMutex _l(mController->mLock); + AutoMutex _l(mController.mLock); if (mLocked.state.alpha != alpha) { mLocked.state.alpha = alpha; @@ -439,7 +451,7 @@ void SpriteController::SpriteImpl::setAlpha(float alpha) { void SpriteController::SpriteImpl::setTransformationMatrix( const SpriteTransformationMatrix& matrix) { - AutoMutex _l(mController->mLock); + AutoMutex _l(mController.mLock); if (mLocked.state.transformationMatrix != matrix) { mLocked.state.transformationMatrix = matrix; @@ -448,7 +460,7 @@ void SpriteController::SpriteImpl::setTransformationMatrix( } void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) { - AutoMutex _l(mController->mLock); + AutoMutex _l(mController.mLock); if (mLocked.state.displayId != displayId) { mLocked.state.displayId = displayId; @@ -461,7 +473,7 @@ void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) { mLocked.state.dirty |= dirty; if (!wasDirty) { - mController->invalidateSpriteLocked(this); + mController.invalidateSpriteLocked(sp<SpriteImpl>::fromExisting(this)); } } diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h index 1f113c045360..04ecb3895aa2 100644 --- a/libs/input/SpriteController.h +++ b/libs/input/SpriteController.h @@ -109,15 +109,19 @@ public: * * Clients are responsible for animating sprites by periodically updating their properties. */ -class SpriteController : public MessageHandler { -protected: - virtual ~SpriteController(); - +class SpriteController { public: using ParentSurfaceProvider = std::function<sp<SurfaceControl>(int /*displayId*/)>; SpriteController(const sp<Looper>& looper, int32_t overlayLayer, ParentSurfaceProvider parent); + SpriteController(const SpriteController&) = delete; + SpriteController& operator=(const SpriteController&) = delete; + virtual ~SpriteController(); - /* Creates a new sprite, initially invisible. */ + /* Initialize the callback for the message handler. */ + void setHandlerController(const std::shared_ptr<SpriteController>& controller); + + /* Creates a new sprite, initially invisible. The lifecycle of the sprite must not extend beyond + * the lifecycle of this SpriteController. */ virtual sp<Sprite> createSprite(); /* Opens or closes a transaction to perform a batch of sprite updates as part of @@ -129,9 +133,12 @@ public: virtual void closeTransaction(); private: - enum { - MSG_UPDATE_SPRITES, - MSG_DISPOSE_SURFACES, + class Handler : public virtual android::MessageHandler { + public: + enum { MSG_UPDATE_SPRITES, MSG_DISPOSE_SURFACES }; + + void handleMessage(const Message& message) override; + std::weak_ptr<SpriteController> spriteController; }; enum { @@ -192,7 +199,7 @@ private: virtual ~SpriteImpl(); public: - explicit SpriteImpl(const sp<SpriteController> controller); + explicit SpriteImpl(SpriteController& controller); virtual void setIcon(const SpriteIcon& icon); virtual void setVisible(bool visible); @@ -220,7 +227,7 @@ private: } private: - sp<SpriteController> mController; + SpriteController& mController; struct Locked { SpriteState state; @@ -245,7 +252,7 @@ private: sp<Looper> mLooper; const int32_t mOverlayLayer; - sp<WeakMessageHandler> mHandler; + sp<Handler> mHandler; ParentSurfaceProvider mParentSurfaceProvider; sp<SurfaceComposerClient> mSurfaceComposerClient; @@ -260,7 +267,6 @@ private: void invalidateSpriteLocked(const sp<SpriteImpl>& sprite); void disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl); - void handleMessage(const Message& message); void doUpdateSprites(); void doDisposeSurfaces(); diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp index d9fe5996bcff..b8de919fbd8c 100644 --- a/libs/input/TouchSpotController.cpp +++ b/libs/input/TouchSpotController.cpp @@ -39,15 +39,15 @@ namespace android { // --- Spot --- -void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y, +void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float newX, float newY, int32_t displayId) { sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); sprite->setAlpha(alpha); sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); - sprite->setPosition(x, y); + sprite->setPosition(newX, newY); sprite->setDisplayId(displayId); - this->x = x; - this->y = y; + x = newX; + y = newY; if (icon != mLastIcon) { mLastIcon = icon; @@ -98,8 +98,8 @@ void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32 #endif std::scoped_lock lock(mLock); - sp<SpriteController> spriteController = mContext.getSpriteController(); - spriteController->openTransaction(); + auto& spriteController = mContext.getSpriteController(); + spriteController.openTransaction(); // Add or move spots for fingers that are down. for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { @@ -125,7 +125,7 @@ void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32 } } - spriteController->closeTransaction(); + spriteController.closeTransaction(); } void TouchSpotController::clearSpots() { @@ -167,7 +167,7 @@ TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t sprite = mLocked.recycledSprites.back(); mLocked.recycledSprites.pop_back(); } else { - sprite = mContext.getSpriteController()->createSprite(); + sprite = mContext.getSpriteController().createSprite(); } // Return the new spot. diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 4eabfb238f4a..8445032293dd 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -26,6 +26,21 @@ cc_test { srcs: [ "PointerController_test.cpp", ], + sanitize: { + hwaddress: true, + undefined: true, + all_undefined: true, + diag: { + undefined: true, + }, + }, + target: { + host: { + sanitize: { + address: true, + }, + }, + }, shared_libs: [ "libandroid_runtime", "libinputservice", diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index 85747514aa03..d9efd3c2fd83 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -148,6 +148,25 @@ void MockPointerControllerPolicyInterface::onPointerDisplayIdChanged(int32_t dis latestPointerDisplayId = displayId; } +class TestPointerController : public PointerController { +public: + TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener, + sp<PointerControllerPolicyInterface> policy, const sp<Looper>& looper, + SpriteController& spriteController) + : PointerController( + policy, looper, spriteController, + /*enabled=*/true, + [®isteredListener](const sp<android::gui::WindowInfosListener>& listener) { + // Register listener + registeredListener = listener; + }, + [®isteredListener](const sp<android::gui::WindowInfosListener>& listener) { + // Unregister listener + if (registeredListener == listener) registeredListener = nullptr; + }) {} + ~TestPointerController() override {} +}; + class PointerControllerTest : public Test { protected: PointerControllerTest(); @@ -157,8 +176,9 @@ protected: sp<MockSprite> mPointerSprite; sp<MockPointerControllerPolicyInterface> mPolicy; - sp<MockSpriteController> mSpriteController; + std::unique_ptr<MockSpriteController> mSpriteController; std::shared_ptr<PointerController> mPointerController; + sp<android::gui::WindowInfosListener> mRegisteredListener; private: void loopThread(); @@ -175,17 +195,18 @@ private: PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>), mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) { - - mSpriteController = new NiceMock<MockSpriteController>(mLooper); + mSpriteController.reset(new NiceMock<MockSpriteController>(mLooper)); mPolicy = new MockPointerControllerPolicyInterface(); EXPECT_CALL(*mSpriteController, createSprite()) .WillOnce(Return(mPointerSprite)); - mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController); + mPointerController = std::make_unique<TestPointerController>(mRegisteredListener, mPolicy, + mLooper, *mSpriteController); } PointerControllerTest::~PointerControllerTest() { + mPointerController.reset(); mRunning.store(false, std::memory_order_relaxed); mThread.join(); } @@ -316,29 +337,16 @@ TEST_F(PointerControllerTest, notifiesPolicyWhenPointerDisplayChanges) { class PointerControllerWindowInfoListenerTest : public Test {}; -class TestPointerController : public PointerController { -public: - TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener, - const sp<Looper>& looper) - : PointerController( - new MockPointerControllerPolicyInterface(), looper, - new NiceMock<MockSpriteController>(looper), - [®isteredListener](const sp<android::gui::WindowInfosListener>& listener) { - // Register listener - registeredListener = listener; - }, - [®isteredListener](const sp<android::gui::WindowInfosListener>& listener) { - // Unregister listener - if (registeredListener == listener) registeredListener = nullptr; - }) {} -}; - TEST_F(PointerControllerWindowInfoListenerTest, doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) { + sp<Looper> looper = new Looper(false); + auto spriteController = NiceMock<MockSpriteController>(looper); sp<android::gui::WindowInfosListener> registeredListener; sp<android::gui::WindowInfosListener> localListenerCopy; + sp<MockPointerControllerPolicyInterface> policy = new MockPointerControllerPolicyInterface(); { - TestPointerController pointerController(registeredListener, new Looper(false)); + TestPointerController pointerController(registeredListener, policy, looper, + spriteController); ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered"; localListenerCopy = registeredListener; } |