feat: WM-Shell QPR1 (not src or shared)

This commit is contained in:
Pun Butrach
2026-01-10 20:48:15 +07:00
parent 383d7737f7
commit cbbafce397
281 changed files with 28915 additions and 9862 deletions
@@ -20,7 +20,6 @@ import android.animation.AnimatorListenerAdapter
import android.content.Intent
import android.graphics.PointF
import android.os.SystemClock
import android.os.Trace
import android.util.Log
import android.view.Display.DEFAULT_DISPLAY
import android.view.View
@@ -30,7 +29,6 @@ import androidx.annotation.UiThread
import androidx.annotation.VisibleForTesting
import com.android.app.displaylib.DisplayRepository
import com.android.app.displaylib.PerDisplayRepository
import com.android.app.tracing.traceSection
import com.android.internal.jank.Cuj
import com.android.launcher3.DeviceProfile
import com.android.launcher3.PagedView
@@ -200,7 +198,6 @@ constructor(
ensureActive()
onCommandFinished(command)
}
}
}
}
@@ -541,7 +538,6 @@ constructor(
command.addListener(recentAnimListener)
taskAnimationManager.notifyRecentsAnimationState(recentAnimListener)
} else {
// Lawnchair-TODO: What name to put in intent here?
val intent =
Intent(interactionHandler.getLaunchIntent())
.putExtra(ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID, gestureState.gestureId)
@@ -551,7 +547,6 @@ constructor(
interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/)
command.addListener(recentAnimListener)
}
//Trace.beginAsyncSection(TRANSITION_NAME, 0)
OverviewCommandHelperProtoLogProxy.logSwitchingViaRecentsAnim(command)
return false
}
@@ -569,7 +564,6 @@ constructor(
) {
OverviewCommandHelperProtoLogProxy.logSwitchingViaRecentsAnimComplete(command)
command.removeListener(handler)
Trace.endAsyncSection(TRANSITION_NAME, 0)
onRecentsViewFocusUpdated(command)
onCommandResult()
}
@@ -20,7 +20,6 @@ import android.graphics.Bitmap
import android.graphics.Matrix
import android.view.View
import android.view.View.OnClickListener
import com.android.app.tracing.traceSection
import com.android.launcher3.Flags.enableRefactorTaskContentView
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.model.data.TaskViewItemInfo
+49 -96
View File
@@ -27,7 +27,9 @@ java_library {
name: "wm_shell_protolog-groups",
srcs: [
"src/com/android/wm/shell/protolog/ShellProtoLogGroup.java",
":protolog-common-src",
],
static_libs: [
"protolog-common-lib",
],
}
@@ -39,28 +41,6 @@ filegroup {
path: "src",
}
// Sources that have no dependencies that can be used directly downstream of this library
// TODO(b/322791067): move these sources to WindowManager-Shell-shared
filegroup {
name: "wm_shell_util-sources",
srcs: [
"src/com/android/wm/shell/animation/Interpolators.java",
"src/com/android/wm/shell/common/bubbles/*.kt",
"src/com/android/wm/shell/common/bubbles/*.java",
"src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt",
"src/com/android/wm/shell/common/split/SplitScreenConstants.java",
"src/com/android/wm/shell/common/TransactionPool.java",
"src/com/android/wm/shell/common/TriangleShape.java",
"src/com/android/wm/shell/common/desktopmode/*.kt",
"src/com/android/wm/shell/draganddrop/DragAndDropConstants.java",
"src/com/android/wm/shell/pip/PipContentOverlay.java",
"src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java",
"src/com/android/wm/shell/sysui/ShellSharedConstants.java",
"src/com/android/wm/shell/util/**/*.java",
],
path: "src",
}
// Aidls which can be used directly downstream of this library
filegroup {
name: "wm_shell-aidls",
@@ -79,74 +59,40 @@ filegroup {
path: "src",
}
genrule {
java_genrule {
name: "wm_shell_protolog_src",
srcs: [
":protolog-impl",
":wm_shell_protolog-groups",
":wm_shell-sources",
":wm_shell_protolog-groups",
],
tools: ["protologtool"],
cmd: "$(location protologtool) transform-protolog-calls " +
"--protolog-class com.android.internal.protolog.common.ProtoLog " +
"--protolog-class com.android.internal.protolog.ProtoLog " +
"--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
"--loggroups-jar $(location :wm_shell_protolog-groups) " +
"--viewer-config-file-path /system_ext/etc/wmshell.protolog.pb " +
"--legacy-viewer-config-file-path /system_ext/etc/wmshell.protolog.json.gz " +
"--legacy-output-file-path /data/misc/wmtrace/shell_log.winscope " +
"--output-srcjar $(out) " +
"$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.srcjar"],
}
genrule {
name: "generate-wm_shell_protolog.json",
srcs: [
":wm_shell_protolog-groups",
":wm_shell-sources",
],
tools: ["protologtool"],
cmd: "$(location protologtool) generate-viewer-config " +
"--protolog-class com.android.internal.protolog.common.ProtoLog " +
"--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
"--loggroups-jar $(location :wm_shell_protolog-groups) " +
"--viewer-config-type json " +
"--viewer-config $(out) " +
"$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.json"],
}
genrule {
java_genrule {
name: "gen-wmshell.protolog.pb",
srcs: [
":wm_shell_protolog-groups",
":wm_shell-sources",
":wm_shell_protolog-groups",
],
tools: ["protologtool"],
cmd: "$(location protologtool) generate-viewer-config " +
"--protolog-class com.android.internal.protolog.common.ProtoLog " +
"--protolog-class com.android.internal.protolog.ProtoLog " +
"--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
"--loggroups-jar $(location :wm_shell_protolog-groups) " +
"--viewer-config-type proto " +
"--viewer-config $(out) " +
"$(locations :wm_shell-sources)",
out: ["wmshell.protolog.pb"],
}
genrule {
name: "protolog.json.gz",
srcs: [":generate-wm_shell_protolog.json"],
out: ["wmshell.protolog.json.gz"],
cmd: "gzip -c < $(in) > $(out)",
}
prebuilt_etc {
name: "wmshell.protolog.json.gz",
system_ext_specific: true,
src: ":protolog.json.gz",
filename_from_src: true,
}
prebuilt_etc {
name: "wmshell.protolog.pb",
system_ext_specific: true,
@@ -166,69 +112,76 @@ java_library {
},
}
filegroup {
name: "wm_shell-shared-aidls",
srcs: [
"shared/**/*.aidl",
],
path: "shared/src",
}
java_library {
name: "WindowManager-Shell-shared",
name: "WindowManager-Shell-lite-proto",
srcs: [
"shared/**/*.java",
"shared/**/*.kt",
":wm_shell-shared-aidls",
],
static_libs: [
"androidx.dynamicanimation_dynamicanimation",
"jsr330",
"src/com/android/wm/shell/desktopmode/education/data/proto/**/*.proto",
"src/com/android/wm/shell/desktopmode/persistence/*.proto",
],
proto: {
type: "lite",
},
}
android_library {
name: "WindowManager-Shell",
srcs: [
":wm_shell_protolog_src",
// TODO(b/168581922) protologtool do not support kotlin(*.kt)
":wm_shell-sources-kt",
":wm_shell-aidls",
":wm_shell-shared-aidls",
":wm_shell-sources-kt",
":wm_shell_protolog_src",
"src/com/android/wm/shell/EventLogTags.logtags",
],
resource_dirs: [
"res",
],
static_libs: [
"androidx.appcompat_appcompat",
"androidx.core_core-animation",
"androidx.core_core-ktx",
"androidx.arch.core_core-runtime",
"androidx.compose.material3_material3",
"androidx-constraintlayout_constraintlayout",
"androidx.dynamicanimation_dynamicanimation",
"androidx.recyclerview_recyclerview",
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
"//frameworks/base/packages/SystemUI/aconfig:com_android_systemui_flags_lib",
"//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
"//frameworks/libs/systemui:iconloader_base",
"com_android_wm_shell_flags_lib",
"com.android.window.flags.window-aconfig-java",
"//frameworks/libs/systemui:msdl",
"//frameworks/libs/systemui:tracinglib-platform",
"//frameworks/libs/systemui:view_capture",
"PlatformAnimationLib",
"WindowManager-Shell-lite-proto",
"WindowManager-Shell-proto",
"WindowManager-Shell-shared",
"perfetto_trace_java_protos",
"androidx-constraintlayout_constraintlayout",
"androidx.appcompat_appcompat",
"androidx.arch.core_core-runtime",
"androidx.compose.material3_material3",
"androidx.core_core-ktx",
"androidx.datastore_datastore",
"androidx.dynamicanimation_dynamicanimation",
"androidx.recyclerview_recyclerview",
"com_android_launcher3_flags_lib",
"com_android_wm_shell_flags_lib",
"dagger2",
"jsr330",
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
"mechanics",
"perfetto_trace_java_protos",
],
libs: [
// Soong fails to automatically add this dependency because all the
// *.kt sources are inside a filegroup.
"kotlin-annotations",
],
flags_packages: [
"com_android_wm_shell_flags",
],
kotlincflags: ["-Xjvm-default=all"],
manifest: "AndroidManifest.xml",
plugins: ["dagger2-compiler"],
use_resource_processor: true,
}
java_defaults {
name: "wmshell_defaults",
required: [
"wmshell.protolog.pb",
],
}
+4 -2
View File
@@ -1,5 +1,7 @@
xutan@google.com
jorgegil@google.com
pbdr@google.com
pragyabajoria@google.com
# Give submodule owners in shell resource approval
per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com, nmusgrave@google.com, pbdr@google.com, tkachenkoi@google.com, mpodolian@google.com, liranb@google.com
per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, lbill@google.com, madym@google.com, vaniadesmonda@google.com, pbdr@google.com, mpodolian@google.com, liranb@google.com, pragyabajoria@google.com, uysalorhan@google.com, gsennton@google.com, mattsziklay@google.com, mdehaini@google.com, peanutbutter@google.com, jeremysim@google.com
per-file res*/*/tv_*.xml = bronger@google.com
+1
View File
@@ -4,6 +4,7 @@ aconfig_declarations {
container: "system",
srcs: [
"multitasking.aconfig",
"automotive.aconfig",
],
}
+2 -1
View File
@@ -1,3 +1,4 @@
# Owners for flag changes
madym@google.com
hwwang@google.com
hwwang@google.com
sqsun@google.com
+160 -26
View File
@@ -1,21 +1,10 @@
# proto-file: build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto
# proto-message: flag_declarations
# Project link: http://gantry/projects/android_platform_multitasking
package: "com.android.wm.shell"
container: "system"
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: "enable_split_contextual"
namespace: "multitasking"
description: "Enables invoking split contextually"
bug: "276361926"
}
flag {
name: "enable_taskbar_navbar_unification"
@@ -25,18 +14,10 @@ flag {
}
flag {
name: "enable_pip2_implementation"
name: "enable_pip2"
namespace: "multitasking"
description: "Enables the new implementation of PiP (PiP2)"
bug: "290220798"
is_fixed_read_only: true
}
flag {
name: "enable_left_right_split_in_portrait"
namespace: "multitasking"
description: "Enables left/right split in portrait"
bug: "291018646"
bug: "311462191"
}
flag {
@@ -102,6 +83,13 @@ flag {
bug: "342245211"
}
flag {
name: "enable_create_any_bubble"
namespace: "multitasking"
description: "Enable UI affordances to create bubbles via launcher app icons"
bug: "385220199"
}
flag {
name: "only_reuse_bubbled_task_when_launched_from_bubble"
namespace: "multitasking"
@@ -113,10 +101,156 @@ flag {
}
flag {
name: "animate_bubble_size_change"
name: "enable_bubble_app_compat_fixes"
namespace: "multitasking"
description: "Turns on the animation for bubble bar icons size change"
bug: "335575529"
description: "Enable fixes for app compat (e.g. insets and layout issues) for bubbles"
bug: "384610402"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_taskbar_on_phones"
namespace: "multitasking"
description: "Enables taskbar on phones"
bug: "348007377"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_shell_top_task_tracking"
namespace: "multitasking"
description: "Enables tracking top tasks from the shell"
bug: "346588978"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_recents_bookend_transition"
namespace: "multitasking"
description: "Use a finish-transition to clean up recents instead of the finish-WCT"
bug: "346588978"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_bubble_to_fullscreen"
namespace: "multitasking"
description: "Enable an option to move bubbles to fullscreen"
bug: "363326492"
}
flag {
name: "enable_flexible_two_app_split"
namespace: "multitasking"
description: "Enables only 2 app 90:10 split"
bug: "349828130"
}
flag {
name: "enable_flexible_split"
namespace: "multitasking"
description: "Enables flexibile split feature for split screen"
bug: "349828130"
}
flag {
name: "task_view_repository"
namespace: "multitasking"
description: "Factor task-view state tracking out of taskviewtransitions"
bug: "384976265"
}
flag {
name: "fix_missing_user_change_callbacks"
namespace: "multitasking"
description: "Fix a race condition that could make Shell miss a user change callback."
bug: "404251029"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_bubble_bar_on_phones"
namespace: "multitasking"
description: "Try out bubble bar on phones"
bug: "394869612"
}
flag {
name: "enable_bubble_task_view_listener"
namespace: "multitasking"
description: "Use the same taskview listener for bubble bar and floating"
bug: "272102927"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_gsf"
namespace: "multitasking"
description: "Applies GSF font styles to multitasking."
bug: "400534660"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_magnetic_split_divider"
namespace: "multitasking"
description: "Makes the split divider snap 'magnetically' to available snap points during drag"
bug: "383631946"
}
flag {
name: "enable_dynamic_insets_for_app_launch"
namespace: "multitasking"
description: "Enables dynamic insets for app launch so the window is properly cropped"
bug: "336511494"
}
flag {
name: "task_view_transitions_refactor"
namespace: "windowing_frontend"
description: "Refactor TaskViewTransitions.startAnimation"
bug: "402454136"
}
flag {
name: "enable_pip_box_shadows"
namespace: "multitasking"
description: "Enables box shadows for PiP"
bug: "367464660"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_bubble_swipe_up_cleanup"
namespace: "multitasking"
description: "Enable clean up of bubbles swipe gesture handling"
bug: "391909607"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
name: "enable_enter_split_remove_bubble"
namespace: "multitasking"
description: "Enable removing a task from bubbles when it is entering split screen"
bug: "396315875"
metadata {
purpose: PURPOSE_BUGFIX
}
+15 -8
View File
@@ -35,7 +35,6 @@ android_app {
android_robolectric_test {
name: "WMShellRobolectricTests",
instrumentation_for: "WindowManagerShellRobolectric",
upstream: true,
java_resource_dirs: [
"robolectric/config",
],
@@ -50,9 +49,14 @@ android_robolectric_test {
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
"mockito-robolectric-prebuilt",
"mockito-robolectric-prebuilt", // mockito deps order matters!
"mockito-kotlin2",
"WMShellTests-utils", // order matters because it depends on mockito-kotlin2
"platform-parametric-runner-lib",
"truth",
"flag-junit-base",
"flag-junit",
"testables",
],
auto_gen_config: true,
}
@@ -63,22 +67,25 @@ android_test {
"src/**/*.kt",
],
static_libs: [
"WMShellTests-utils",
"WindowManager-Shell",
"junit",
"androidx.core_core-animation-testing",
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
"androidx.test.rules",
"androidx.test.runner",
"frameworks-base-testutils",
"junit",
"mockito-kotlin2",
"mockito-target-extended-minus-junit4",
"truth",
"platform-parametric-runner-lib",
"platform-test-annotations",
"platform-test-rules",
"testables",
"truth",
],
libs: [
"android.test.base",
"android.test.runner",
"android.test.base.stubs.system",
"android.test.runner.stubs.system",
],
jni_libs: [
"libdexmakerjvmtiagent",
@@ -1,8 +1,23 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell.multivalenttests">
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
<uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/>
<application android:debuggable="true" android:supportsRtl="true" >
<uses-library android:name="android.test.runner" />
<activity android:name="com.android.wm.shell.bubbles.bar.BubbleBarAnimationHelperTest$TestActivity"
android:exported="true"/>
<activity android:name="com.android.wm.shell.bubbles.bar.BubbleBarMenuViewControllerTest$TestActivity"
android:exported="true"/>
<activity android:name=".bubbles.TestActivity"
android:allowEmbedded="true"
android:documentLaunchMode="always"
android:excludeFromRecents="true"
android:exported="false"
android:resizeableActivity="true" />
</application>
<instrumentation
@@ -1,3 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell.multivalenttests">
<application>
<activity android:name="com.android.wm.shell.bubbles.bar.BubbleBarAnimationHelperTest$TestActivity"
android:exported="true"/>
<activity android:name="com.android.wm.shell.bubbles.bar.BubbleBarMenuViewControllerTest$TestActivity"
android:exported="true"/>
</application>
</manifest>
@@ -1,2 +1,3 @@
sdk=NEWEST_SDK
graphicsMode=NATIVE
@@ -27,10 +27,11 @@ import android.view.WindowManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.protolog.common.ProtoLog
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.DeviceConfig
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Before
@@ -154,19 +155,19 @@ class BubblePositionerTest {
/** Test that the default resting position on tablet is middle right. */
@Test
fun testGetDefaultPosition_appBubble_onTablet() {
fun testGetDefaultPosition_noteBubble_onTablet() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */)
val startPosition = positioner.getDefaultStartPosition(true /* isNoteBubble */)
assertThat(startPosition.x).isEqualTo(allowableStackRegion.right)
assertThat(startPosition.y).isEqualTo(defaultYPosition)
}
@Test
fun testGetRestingPosition_appBubble_onTablet_RTL() {
fun testGetRestingPosition_noteBubble_onTablet_RTL() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */)
val startPosition = positioner.getDefaultStartPosition(true /* isNoteBubble */)
assertThat(startPosition.x).isEqualTo(allowableStackRegion.left)
assertThat(startPosition.y).isEqualTo(defaultYPosition)
}
@@ -221,7 +222,7 @@ class BubblePositionerTest {
positioner.setShowingInBubbleBar(true)
positioner.update(deviceConfig)
positioner.bubbleBarTopOnScreen = 2500
positioner.updateBubbleBarTopOnScreen(100)
val spaceBetweenTopInsetAndBubbleBarInLandscape = 1680
val expandedViewVerticalSpacing =
@@ -247,7 +248,7 @@ class BubblePositionerTest {
)
positioner.setShowingInBubbleBar(true)
positioner.update(deviceConfig)
positioner.bubbleBarTopOnScreen = 2500
positioner.updateBubbleBarTopOnScreen(100)
val spaceBetweenTopInsetAndBubbleBarInLandscape = 180
val expandedViewSpacing =
@@ -268,7 +269,8 @@ class BubblePositionerTest {
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
val bubble =
Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT)
}
@@ -294,6 +296,7 @@ class BubblePositionerTest {
0 /* taskId */,
null /* locus */,
true /* isDismissable */,
directExecutor(),
directExecutor()
) {}
@@ -322,6 +325,7 @@ class BubblePositionerTest {
0 /* taskId */,
null /* locus */,
true /* isDismissable */,
directExecutor(),
directExecutor()
) {}
@@ -416,7 +420,8 @@ class BubblePositionerTest {
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
val bubble =
Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
// This bubble will have max height so it'll always be top aligned
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
@@ -433,7 +438,8 @@ class BubblePositionerTest {
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
val bubble =
Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
// Always top aligned in phone portrait
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
@@ -452,7 +458,8 @@ class BubblePositionerTest {
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
val bubble =
Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
// This bubble will have max height which is always top aligned on small tablets
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
@@ -470,7 +477,8 @@ class BubblePositionerTest {
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
val bubble =
Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
// This bubble will have max height which is always top aligned on small tablets
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
@@ -489,7 +497,8 @@ class BubblePositionerTest {
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
val bubble =
Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
// This bubble will have max height which is always top aligned on landscape, large tablet
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
@@ -507,7 +516,8 @@ class BubblePositionerTest {
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
val bubble =
Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
val manageButtonHeight =
context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height)
@@ -575,26 +585,115 @@ class BubblePositionerTest {
@Test
fun testGetBubbleBarExpandedViewBounds_onLeft() {
testGetBubbleBarExpandedViewBounds(onLeft = true, isOverflow = false)
verifyGetBubbleBarExpandedViewBounds(onLeft = true, isOverflow = false)
}
@Test
fun testGetBubbleBarExpandedViewBounds_onRight() {
testGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = false)
verifyGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = false)
}
@Test
fun testGetBubbleBarExpandedViewBounds_isOverflow_onLeft() {
testGetBubbleBarExpandedViewBounds(onLeft = true, isOverflow = true)
verifyGetBubbleBarExpandedViewBounds(onLeft = true, isOverflow = true)
}
@Test
fun testGetBubbleBarExpandedViewBounds_isOverflow_onRight() {
testGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = true)
verifyGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = true)
}
private fun testGetBubbleBarExpandedViewBounds(onLeft: Boolean, isOverflow: Boolean) {
positioner.setShowingInBubbleBar(true)
@Test
fun testGetTaskViewRestBounds_phone() {
val deviceConfig =
defaultDeviceConfig.copy(
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val padding = context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
val top = positioner.expandedViewYTopAligned
val left = positioner.insets.left + padding
val right = positioner.screenRect.right - positioner.insets.right - padding
val bottom = top + positioner.getMaxExpandedViewHeight(false)
val rect = Rect()
positioner.getTaskViewRestBounds(rect)
assertThat(rect).isEqualTo(Rect(left, top, right, bottom))
}
@Test
fun getExpandedViewContainerPadding_largeScreen_fitsMaxViewWidth() {
val expandedViewWidth = context.resources.getDimensionPixelSize(
R.dimen.bubble_expanded_view_largescreen_width
)
// set the screen size so that it is wide enough to fit the maximum width size
val screenWidth = expandedViewWidth * 2
positioner.update(
defaultDeviceConfig.copy(
windowBounds = Rect(0, 0, screenWidth, 2000),
isLargeScreen = true,
isLandscape = false
)
)
val paddings =
positioner.getExpandedViewContainerPadding(/* onLeft= */ true, /* isOverflow= */ false)
val padding = context.resources.getDimensionPixelSize(
R.dimen.bubble_expanded_view_largescreen_landscape_padding
)
val right = screenWidth - expandedViewWidth - padding
assertThat(paddings).isEqualTo(intArrayOf(padding - positioner.pointerSize, 0, right, 0))
}
@Test
fun getExpandedViewContainerPadding_largeScreen_doesNotFitMaxViewWidth() {
positioner.update(
defaultDeviceConfig.copy(
windowBounds = Rect(0, 0, 600, 2000),
isLargeScreen = true,
isLandscape = false
)
)
val paddings =
positioner.getExpandedViewContainerPadding(/* onLeft= */ true, /* isOverflow= */ false)
val padding = context.resources.getDimensionPixelSize(
R.dimen.bubble_expanded_view_largescreen_landscape_padding
)
// the screen is not wide enough to fit the maximum width size, so the view fills the screen
// minus left and right padding
assertThat(paddings).isEqualTo(intArrayOf(padding - positioner.pointerSize, 0, padding, 0))
}
@Test
fun getExpandedViewContainerPadding_smallTablet() {
val screenWidth = 500
positioner.update(
defaultDeviceConfig.copy(
windowBounds = Rect(0, 0, screenWidth, 2000),
isLargeScreen = true,
isSmallTablet = true,
isLandscape = false
)
)
val paddings =
positioner.getExpandedViewContainerPadding(/* onLeft= */ true, /* isOverflow= */ false)
// for small tablets, the view width is set to be 0.72 * screen width
val viewWidth = (screenWidth * 0.72).toInt()
val padding = (screenWidth - viewWidth) / 2
assertThat(paddings).isEqualTo(intArrayOf(padding - positioner.pointerSize, 0, padding, 0))
}
private fun verifyGetBubbleBarExpandedViewBounds(onLeft: Boolean, isOverflow: Boolean) {
positioner.isShowingInBubbleBar = true
positioner.bubbleBarLocation = if (onLeft) {
BubbleBarLocation.LEFT
} else {
BubbleBarLocation.RIGHT
}
val windowBounds = Rect(0, 0, 2000, 2600)
val insets = Insets.of(10, 20, 5, 15)
val deviceConfig =
@@ -607,7 +706,7 @@ class BubblePositionerTest {
positioner.update(deviceConfig)
val bubbleBarHeight = 100
positioner.bubbleBarTopOnScreen = windowBounds.bottom - insets.bottom - bubbleBarHeight
positioner.updateBubbleBarTopOnScreen(bubbleBarHeight)
val expandedViewPadding =
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
@@ -632,8 +731,14 @@ class BubblePositionerTest {
val bounds = Rect()
positioner.getBubbleBarExpandedViewBounds(onLeft, isOverflow, bounds)
assertThat(bounds).isEqualTo(expectedBounds)
if (!isOverflow) {
val bounds2 = Rect()
// In bubble bar mode this should return the same bounds
positioner.getTaskViewRestBounds(bounds2)
assertThat(bounds2).isEqualTo(expectedBounds)
}
}
private val defaultYPosition: Float
@@ -23,36 +23,39 @@ import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Icon
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.view.IWindowManager
import android.view.WindowManager
import android.view.WindowManagerGlobal
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.common.ProtoLog
import com.android.internal.protolog.ProtoLog
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.Flags
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubbleStackView.SurfaceSynchronizer
import com.android.wm.shell.bubbles.Bubbles.BubbleExpandListener
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
import com.android.wm.shell.common.FloatingContentCoordinator
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.TestShellExecutor
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.After
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
@@ -66,14 +69,16 @@ class BubbleStackViewTest {
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var positioner: BubblePositioner
private lateinit var bubbleLogger: BubbleLogger
private lateinit var iconFactory: BubbleIconFactory
private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
private lateinit var bubbleStackView: BubbleStackView
private lateinit var shellExecutor: ShellExecutor
private lateinit var windowManager: IWindowManager
private lateinit var shellExecutor: TestShellExecutor
private lateinit var windowManager: WindowManager
private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory
private lateinit var bubbleData: BubbleData
private lateinit var bubbleStackViewManager: FakeBubbleStackViewManager
private lateinit var surfaceSynchronizer: FakeSurfaceSynchronizer
private var sysuiProxy = mock<SysuiProxy>()
@Before
@@ -81,9 +86,8 @@ class BubbleStackViewTest {
PhysicsAnimatorTestUtils.prepareForTest()
// Disable protolog tool when running the tests from studio
ProtoLog.REQUIRE_PROTOLOGTOOL = false
windowManager = WindowManagerGlobal.getWindowManagerService()!!
shellExecutor = TestShellExecutor()
val windowManager = context.getSystemService(WindowManager::class.java)
windowManager = context.getSystemService(WindowManager::class.java)
iconFactory =
BubbleIconFactory(
context,
@@ -95,24 +99,27 @@ class BubbleStackViewTest {
)
)
positioner = BubblePositioner(context, windowManager)
bubbleLogger = BubbleLogger(UiEventLoggerFake())
bubbleData =
BubbleData(
context,
BubbleLogger(UiEventLoggerFake()),
bubbleLogger,
positioner,
BubbleEducationController(context),
shellExecutor,
shellExecutor
)
bubbleStackViewManager = FakeBubbleStackViewManager()
expandedViewManager = FakeBubbleExpandedViewManager()
bubbleTaskViewFactory = FakeBubbleTaskViewFactory()
bubbleTaskViewFactory = FakeBubbleTaskViewFactory(context, shellExecutor)
surfaceSynchronizer = FakeSurfaceSynchronizer()
bubbleStackView =
BubbleStackView(
context,
bubbleStackViewManager,
positioner,
bubbleData,
null,
surfaceSynchronizer,
FloatingContentCoordinator(),
{ sysuiProxy },
shellExecutor
@@ -165,6 +172,7 @@ class BubbleStackViewTest {
// This will eventually propagate an update back to the stack view, but setting the
// entire pipeline is outside the scope of a unit test.
assertThat(bubbleData.isExpanded).isTrue()
shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
@@ -173,8 +181,207 @@ class BubbleStackViewTest {
assertThat(lastUpdate!!.expanded).isTrue()
}
@Test
fun expandStack_imeHidden() {
val bubble = createAndInflateBubble()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
positioner.setImeVisible(false, 0)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// simulate a request from the bubble data listener to expand the stack
bubbleStackView.isExpanded = true
verify(sysuiProxy).onStackExpandChanged(true)
shellExecutor.flushAll()
}
assertThat(bubbleStackViewManager.onImeHidden).isNull()
}
@Test
fun collapseStack_imeHidden() {
val bubble = createAndInflateBubble()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
positioner.setImeVisible(false, 0)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// simulate a request from the bubble data listener to expand the stack
bubbleStackView.isExpanded = true
verify(sysuiProxy).onStackExpandChanged(true)
shellExecutor.flushAll()
}
assertThat(bubbleStackViewManager.onImeHidden).isNull()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// simulate a request from the bubble data listener to collapse the stack
bubbleStackView.isExpanded = false
verify(sysuiProxy).onStackExpandChanged(false)
shellExecutor.flushAll()
}
assertThat(bubbleStackViewManager.onImeHidden).isNull()
}
@Test
fun expandStack_waitsForIme() {
val bubble = createAndInflateBubble()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
positioner.setImeVisible(true, 100)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// simulate a request from the bubble data listener to expand the stack
bubbleStackView.isExpanded = true
}
val onImeHidden = bubbleStackViewManager.onImeHidden
assertThat(onImeHidden).isNotNull()
verify(sysuiProxy, never()).onStackExpandChanged(any())
positioner.setImeVisible(false, 0)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
onImeHidden!!.run()
verify(sysuiProxy).onStackExpandChanged(true)
shellExecutor.flushAll()
}
}
@Test
fun collapseStack_waitsForIme() {
val bubble = createAndInflateBubble()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
positioner.setImeVisible(true, 100)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// simulate a request from the bubble data listener to expand the stack
bubbleStackView.isExpanded = true
}
var onImeHidden = bubbleStackViewManager.onImeHidden
assertThat(onImeHidden).isNotNull()
verify(sysuiProxy, never()).onStackExpandChanged(any())
positioner.setImeVisible(false, 0)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
onImeHidden!!.run()
verify(sysuiProxy).onStackExpandChanged(true)
shellExecutor.flushAll()
}
bubbleStackViewManager.onImeHidden = null
positioner.setImeVisible(true, 100)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// simulate a request from the bubble data listener to collapse the stack
bubbleStackView.isExpanded = false
}
onImeHidden = bubbleStackViewManager.onImeHidden
assertThat(onImeHidden).isNotNull()
verify(sysuiProxy, never()).onStackExpandChanged(false)
positioner.setImeVisible(false, 0)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
onImeHidden!!.run()
verify(sysuiProxy).onStackExpandChanged(false)
shellExecutor.flushAll()
}
}
@EnableFlags(Flags.FLAG_ENABLE_BUBBLE_SWIPE_UP_CLEANUP)
@Test
fun expandStack_clearsImeRunnable() {
val bubble = createAndInflateBubble()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
// Set up a pending runnable to be cleared
bubbleStackViewManager.onImeHidden = Runnable {
fail("IME runnable should not be called when IME is hidden")
}
positioner.setImeVisible(false, 0)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// simulate a request from the bubble data listener to expand the stack
bubbleStackView.isExpanded = true
verify(sysuiProxy).onStackExpandChanged(true)
shellExecutor.flushAll()
}
// Ime runnable is reset
assertThat(bubbleStackViewManager.onImeHidden).isNull()
}
@EnableFlags(Flags.FLAG_ENABLE_BUBBLE_SWIPE_UP_CLEANUP)
@Test
fun collapseStack_clearsImeRunnable() {
val bubble = createAndInflateBubble()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
positioner.setImeVisible(false, 0)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// simulate a request from the bubble data listener to expand the stack
bubbleStackView.isExpanded = true
verify(sysuiProxy).onStackExpandChanged(true)
shellExecutor.flushAll()
}
// Set up a pending runnable to be cleared
bubbleStackViewManager.onImeHidden = Runnable {
fail("IME runnable should not be called when IME is hidden")
}
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// simulate a request from the bubble data listener to collapse the stack
bubbleStackView.isExpanded = false
verify(sysuiProxy).onStackExpandChanged(false)
shellExecutor.flushAll()
}
// Check that the runnable is cleared
assertThat(bubbleStackViewManager.onImeHidden).isNull()
}
@Test
fun tapDifferentBubble_shouldReorder() {
surfaceSynchronizer.isActive = false
val bubble1 = createAndInflateChatBubble(key = "bubble1")
val bubble2 = createAndInflateChatBubble(key = "bubble2")
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -203,6 +410,7 @@ class BubbleStackViewTest {
bubbleStackView.setSelectedBubble(bubble2)
bubbleStackView.isExpanded = true
shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
@@ -220,6 +428,7 @@ class BubbleStackViewTest {
// tap on bubble1 to select it
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubble1.iconView!!.performClick()
shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
@@ -230,6 +439,7 @@ class BubbleStackViewTest {
// listener wired up.
bubbleStackView.setSelectedBubble(bubble1)
bubble1.iconView!!.performClick()
shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
@@ -241,6 +451,147 @@ class BubbleStackViewTest {
.inOrder()
}
@Test
fun tapDifferentBubble_imeVisible_shouldWaitForIme() {
val bubble1 = createAndInflateChatBubble(key = "bubble1")
val bubble2 = createAndInflateChatBubble(key = "bubble2")
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble1)
bubbleStackView.addBubble(bubble2)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(2)
assertThat(bubbleData.bubbles).hasSize(2)
assertThat(bubbleData.selectedBubble).isEqualTo(bubble2)
assertThat(bubble2.iconView).isNotNull()
val expandListener = FakeBubbleExpandListener()
bubbleStackView.setExpandListener(expandListener)
var lastUpdate: BubbleData.Update? = null
val semaphore = Semaphore(0)
val listener =
BubbleData.Listener { update ->
lastUpdate = update
semaphore.release()
}
bubbleData.setListener(listener)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubble2.iconView!!.performClick()
assertThat(bubbleData.isExpanded).isTrue()
bubbleStackView.setSelectedBubble(bubble2)
bubbleStackView.isExpanded = true
shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(lastUpdate!!.expanded).isTrue()
assertThat(lastUpdate!!.bubbles.map { it.key })
.containsExactly("bubble2", "bubble1")
.inOrder()
// wait for idle to allow the animation to start
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
// wait for the expansion animation to complete before interacting with the bubbles
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
AnimatableScaleMatrix.SCALE_X, AnimatableScaleMatrix.SCALE_Y)
// make the IME visible and tap on bubble1 to select it
InstrumentationRegistry.getInstrumentation().runOnMainSync {
positioner.setImeVisible(true, 100)
bubble1.iconView!!.performClick()
// we have to set the selected bubble in the stack view manually because we don't have a
// listener wired up.
bubbleStackView.setSelectedBubble(bubble1)
shellExecutor.flushAll()
}
val onImeHidden = bubbleStackViewManager.onImeHidden
assertThat(onImeHidden).isNotNull()
assertThat(expandListener.bubblesExpandedState).isEqualTo(mapOf("bubble2" to true))
InstrumentationRegistry.getInstrumentation().runOnMainSync {
onImeHidden!!.run()
shellExecutor.flushAll()
}
assertThat(expandListener.bubblesExpandedState)
.isEqualTo(mapOf("bubble1" to true, "bubble2" to false))
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
}
@Test
fun tapDifferentBubble_imeHidden_updatesImmediately() {
val bubble1 = createAndInflateChatBubble(key = "bubble1")
val bubble2 = createAndInflateChatBubble(key = "bubble2")
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble1)
bubbleStackView.addBubble(bubble2)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(2)
assertThat(bubbleData.bubbles).hasSize(2)
assertThat(bubbleData.selectedBubble).isEqualTo(bubble2)
assertThat(bubble2.iconView).isNotNull()
val expandListener = FakeBubbleExpandListener()
bubbleStackView.setExpandListener(expandListener)
var lastUpdate: BubbleData.Update? = null
val semaphore = Semaphore(0)
val listener =
BubbleData.Listener { update ->
lastUpdate = update
semaphore.release()
}
bubbleData.setListener(listener)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubble2.iconView!!.performClick()
assertThat(bubbleData.isExpanded).isTrue()
bubbleStackView.setSelectedBubble(bubble2)
bubbleStackView.isExpanded = true
shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(lastUpdate!!.expanded).isTrue()
assertThat(lastUpdate!!.bubbles.map { it.key })
.containsExactly("bubble2", "bubble1")
.inOrder()
// wait for idle to allow the animation to start
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
// wait for the expansion animation to complete before interacting with the bubbles
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
AnimatableScaleMatrix.SCALE_X, AnimatableScaleMatrix.SCALE_Y)
// make the IME hidden and tap on bubble1 to select it
InstrumentationRegistry.getInstrumentation().runOnMainSync {
positioner.setImeVisible(false, 0)
bubble1.iconView!!.performClick()
// we have to set the selected bubble in the stack view manually because we don't have a
// listener wired up.
bubbleStackView.setSelectedBubble(bubble1)
shellExecutor.flushAll()
}
val onImeHidden = bubbleStackViewManager.onImeHidden
assertThat(onImeHidden).isNull()
assertThat(expandListener.bubblesExpandedState)
.isEqualTo(mapOf("bubble1" to true, "bubble2" to false))
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
}
@EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun testCreateStackView_noOverflowContents_noOverflow() {
@@ -350,6 +701,122 @@ class BubbleStackViewTest {
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
}
@Test
fun removeFromWindow_stopMonitoringSwipeUpGesture() {
bubbleStackView = spy(bubbleStackView)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// No way to add to window in the test environment right now so just pretend
bubbleStackView.onDetachedFromWindow()
}
verify(bubbleStackView).stopMonitoringSwipeUpGesture()
}
@Test
fun animateExpand_expandRunsRunnable() {
bubbleStackView = spy(bubbleStackView)
val bubble = createAndInflateChatBubble(key = "bubble")
assertThat(bubble.expandedView).isNotNull()
var afterTransitionRan = false
val semaphore = Semaphore(0)
// Expand animation runs on a delay so wait for it.
val runnable = Runnable {
afterTransitionRan = true
semaphore.release()
}
assertThat(bubbleStackView.isExpanded).isFalse()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
bubbleStackView.setSelectedBubble(bubble)
bubbleStackView.animateExpand(null, runnable)
bubbleStackView.isExpanded = true
shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubbleStackView.isExpanded).isTrue()
assertThat(afterTransitionRan).isTrue()
}
@Test
fun animateExpand_switchRunsRunnable() {
bubbleStackView = spy(bubbleStackView)
val bubble = createAndInflateChatBubble(key = "bubble")
val bubble2 = createAndInflateChatBubble(key = "bubble2")
var afterTransitionRan = false
val semaphore = Semaphore(0)
// Expand animation runs on a delay so wait for it.
val runnable = Runnable {
afterTransitionRan = true
semaphore.release()
}
assertThat(bubbleStackView.isExpanded).isFalse()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
bubbleStackView.addBubble(bubble2)
bubbleStackView.setSelectedBubble(bubble)
bubbleStackView.isExpanded = true
shellExecutor.flushAll()
}
assertThat(bubbleStackView.isExpanded).isTrue()
assertThat(bubbleStackView.expandedBubble!!.key).isEqualTo(bubble.key)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.animateExpand(null, runnable)
bubbleStackView.setSelectedBubble(bubble2)
shellExecutor.flushAll()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubbleStackView.isExpanded).isTrue()
assertThat(bubbleStackView.expandedBubble!!.key).isEqualTo(bubble2.key)
assertThat(afterTransitionRan).isTrue()
}
@Test
fun canExpandView_true_triggersContinueExpand() {
bubbleStackView = spy(bubbleStackView)
val bubble = createAndInflateChatBubble(key = "bubble")
val bubbleTransition = mock<BubbleTransitions.BubbleTransition>()
bubble.preparingTransition = bubbleTransition
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
}
assertThat(bubbleStackView.isExpanded).isFalse()
assertThat(bubbleStackView.canExpandView(bubble)).isTrue()
verify(bubbleTransition).continueExpand()
}
@Test
fun canExpandView_false() {
bubbleStackView = spy(bubbleStackView)
val bubble = createAndInflateChatBubble(key = "bubble")
val bubbleTransition = mock<BubbleTransitions.BubbleTransition>()
bubble.preparingTransition = bubbleTransition
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
bubbleStackView.setSelectedBubble(bubble)
bubbleStackView.isExpanded = true
shellExecutor.flushAll()
}
assertThat(bubbleStackView.isExpanded).isTrue()
assertThat(bubbleStackView.expandedBubble!!.key).isEqualTo(bubble.key)
assertThat(bubbleStackView.canExpandView(bubble)).isFalse()
verify(bubbleTransition, never()).continueExpand()
}
private fun createAndInflateChatBubble(key: String): Bubble {
val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build()
@@ -363,6 +830,7 @@ class BubbleStackViewTest {
/* taskId= */ 0,
"locus",
/* isDismissable= */ true,
directExecutor(),
directExecutor()
) {}
inflateBubble(bubble)
@@ -372,7 +840,8 @@ class BubbleStackViewTest {
private fun createAndInflateBubble(): Bubble {
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor())
val bubble =
Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor(), directExecutor())
inflateBubble(bubble)
return bubble
}
@@ -393,6 +862,7 @@ class BubbleStackViewTest {
bubbleStackView,
null,
iconFactory,
FakeBubbleAppInfoProvider(),
false
)
@@ -401,6 +871,7 @@ class BubbleStackViewTest {
}
private class FakeBubbleStackViewManager : BubbleStackViewManager {
var onImeHidden: Runnable? = null
override fun onAllBubblesAnimatedOut() {}
@@ -408,55 +879,26 @@ class BubbleStackViewTest {
override fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>) {}
override fun hideCurrentInputMethod() {}
}
private class TestShellExecutor : ShellExecutor {
override fun execute(runnable: Runnable) {
runnable.run()
override fun hideCurrentInputMethod(onImeHidden: Runnable?) {
this.onImeHidden = onImeHidden
}
override fun executeDelayed(r: Runnable, delayMillis: Long) {
r.run()
}
override fun removeCallbacks(r: Runnable?) {}
override fun hasCallback(r: Runnable): Boolean = false
}
private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
override fun create(): BubbleTaskView {
val taskViewTaskController = mock<TaskViewTaskController>()
val taskView = TaskView(context, taskViewTaskController)
return BubbleTaskView(taskView, shellExecutor)
override fun clearImeHiddenRunnable() {
this.onImeHidden = null
}
}
private inner class FakeBubbleExpandedViewManager : BubbleExpandedViewManager {
private class FakeBubbleExpandListener : BubbleExpandListener {
val bubblesExpandedState = mutableMapOf<String, Boolean>()
override fun onBubbleExpandChanged(isExpanding: Boolean, key: String) {
bubblesExpandedState[key] = isExpanding
}
}
override val overflowBubbles: List<Bubble>
get() = emptyList()
override fun setOverflowListener(listener: BubbleData.Listener) {}
override fun collapseStack() {}
override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
override fun promoteBubbleFromOverflow(bubble: Bubble) {}
override fun removeBubble(key: String, reason: Int) {}
override fun dismissBubble(bubble: Bubble, reason: Int) {}
override fun setAppBubbleTaskId(key: String, taskId: Int) {}
override fun isStackExpanded(): Boolean = false
override fun isShowingAsBubbleBar(): Boolean = false
override fun hideCurrentInputMethod() {}
private class FakeSurfaceSynchronizer : SurfaceSynchronizer {
var isActive = true
override fun syncSurfaceAndRun(callback: Runnable) {
if (isActive) callback.run()
}
}
}
@@ -16,39 +16,45 @@
package com.android.wm.shell.bubbles
import android.app.ActivityManager
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.content.ComponentName
import android.content.Context
import android.platform.test.flag.junit.FlagsParameterization
import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags.FLAG_EXCLUDE_TASK_FROM_RECENTS
import com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
import com.android.wm.shell.taskview.TaskView
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleTaskViewTest {
@RunWith(ParameterizedAndroidJunit4::class)
class BubbleTaskViewTest(flags: FlagsParameterization) {
@get:Rule
val setFlagsRule = SetFlagsRule(flags)
private lateinit var bubbleTaskView: BubbleTaskView
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var taskView: TaskView
@Before
fun setUp() {
taskView = mock()
bubbleTaskView = BubbleTaskView(taskView, directExecutor())
}
private val componentName = ComponentName(context, "TestClass")
private val taskView = mock<TaskView>()
private val bubbleTaskView = BubbleTaskView(taskView, directExecutor())
@Test
fun onTaskCreated_updatesState() {
val componentName = ComponentName(context, "TestClass")
bubbleTaskView.listener.onTaskCreated(123, componentName)
assertThat(bubbleTaskView.taskId).isEqualTo(123)
@@ -68,25 +74,64 @@ class BubbleTaskViewTest {
}
bubbleTaskView.delegateListener = delegateListener
val componentName = ComponentName(context, "TestClass")
bubbleTaskView.listener.onTaskCreated(123, componentName)
bubbleTaskView.listener.onTaskCreated(123 /* taskId */, componentName)
assertThat(actualTaskId).isEqualTo(123)
assertThat(actualComponentName).isEqualTo(componentName)
}
@Test
fun cleanup_invalidTaskId_doesNotRemoveTask() {
fun cleanup_invalidTaskId_removesTask() {
bubbleTaskView.cleanup()
verify(taskView, never()).removeTask()
verify(taskView).removeTask()
}
@Test
fun cleanup_validTaskId_removesTask() {
val componentName = ComponentName(context, "TestClass")
bubbleTaskView.listener.onTaskCreated(123, componentName)
bubbleTaskView.listener.onTaskCreated(123 /* taskId */, componentName)
bubbleTaskView.cleanup()
verify(taskView).removeTask()
}
@Test
fun cleanup_noneFullscreenTask_removesTask() {
bubbleTaskView.listener.onTaskCreated(123 /* taskId */, componentName)
bubbleTaskView.cleanup()
verify(taskView, never()).unregisterTask()
verify(taskView).removeTask()
}
@Test
fun cleanup_fullscreenTask_removesOrUnregistersTask() {
val fullScreenTaskInfo = ActivityManager.RunningTaskInfo().apply {
configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
}
taskView.stub {
on { taskInfo } doReturn fullScreenTaskInfo
}
bubbleTaskView.listener.onTaskCreated(123 /* taskId */, componentName)
bubbleTaskView.cleanup()
if (BubbleAnythingFlagHelper.enableCreateAnyBubbleWithForceExcludedFromRecents()) {
verify(taskView).unregisterTask()
verify(taskView, never()).removeTask()
} else {
verify(taskView, never()).unregisterTask()
verify(taskView).removeTask()
}
}
companion object {
@JvmStatic
@Parameters(name = "{0}")
fun getParams() = FlagsParameterization.allCombinationsOf(
FLAG_ENABLE_CREATE_ANY_BUBBLE,
FLAG_EXCLUDE_TASK_FROM_RECENTS,
)
}
}
@@ -27,16 +27,16 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.protolog.common.ProtoLog
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.bubbles.DeviceConfig
import com.android.wm.shell.common.bubbles.BaseBubblePinController
import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
import com.android.wm.shell.shared.bubbles.BaseBubblePinController
import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
import com.android.wm.shell.shared.bubbles.DeviceConfig
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -74,6 +74,7 @@ class BubbleExpandedViewPinControllerTest {
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
ProtoLog.init()
container = FrameLayout(context)
val windowManager = context.getSystemService(WindowManager::class.java)
positioner = BubblePositioner(context, windowManager)
@@ -85,11 +86,10 @@ class BubbleExpandedViewPinControllerTest {
isSmallTablet = false,
isLandscape = true,
isRtl = false,
insets = Insets.of(10, 20, 30, 40)
insets = Insets.of(10, 20, 30, 40),
)
positioner.update(deviceConfig)
positioner.bubbleBarTopOnScreen =
SCREEN_HEIGHT - deviceConfig.insets.bottom - BUBBLE_BAR_HEIGHT
positioner.updateBubbleBarTopOnScreen(BUBBLE_BAR_HEIGHT)
controller = BubbleExpandedViewPinController(context, container, positioner)
testListener = TestLocationChangeListener()
controller.setListener(testListener)
@@ -407,12 +407,26 @@ class BubbleExpandedViewPinControllerTest {
assertThat(testListener.locationReleases).containsExactly(RIGHT)
}
/** Send drag start event when on left */
@Test
fun start_onLeft_sendStartEventOnLeft() {
getInstrumentation().runOnMainSync { controller.onDragStart(initialLocationOnLeft = true) }
assertThat(testListener.locationStart).containsExactly(LEFT)
}
/** Send drag start event when on right */
@Test
fun start_onRight_sendStartEventOnRight() {
getInstrumentation().runOnMainSync { controller.onDragStart(initialLocationOnLeft = false) }
assertThat(testListener.locationStart).containsExactly(RIGHT)
}
private fun getExpectedDropTargetBoundsOnLeft(): Rect =
Rect().also {
positioner.getBubbleBarExpandedViewBounds(
true /* onLeft */,
false /* isOverflowExpanded */,
it
it,
)
}
@@ -421,7 +435,7 @@ class BubbleExpandedViewPinControllerTest {
positioner.getBubbleBarExpandedViewBounds(
false /* onLeft */,
false /* isOverflowExpanded */,
it
it,
)
}
@@ -446,8 +460,14 @@ class BubbleExpandedViewPinControllerTest {
}
internal class TestLocationChangeListener : BaseBubblePinController.LocationChangeListener {
val locationStart = mutableListOf<BubbleBarLocation>()
val locationChanges = mutableListOf<BubbleBarLocation>()
val locationReleases = mutableListOf<BubbleBarLocation>()
override fun onStart(location: BubbleBarLocation) {
locationStart.add(location)
}
override fun onChange(location: BubbleBarLocation) {
locationChanges.add(location)
}
@@ -1,99 +0,0 @@
// Copyright (C) 2019 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
default_team: "trendy_team_multitasking_windowing",
}
android_app {
name: "WindowManagerShellRobolectric",
platform_apis: true,
static_libs: [
"WindowManager-Shell",
],
manifest: "AndroidManifestRobolectric.xml",
use_resource_processor: true,
}
android_robolectric_test {
name: "WMShellRobolectricTests",
instrumentation_for: "WindowManagerShellRobolectric",
upstream: true,
java_resource_dirs: [
"robolectric/config",
],
srcs: [
"src/**/*.kt",
],
// TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed.
exclude_srcs: ["src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"],
static_libs: [
"junit",
"androidx.core_core-animation-testing",
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
"mockito-robolectric-prebuilt",
"mockito-kotlin2",
"truth",
],
auto_gen_config: true,
}
android_test {
name: "WMShellMultivalentTestsOnDevice",
srcs: [
"src/**/*.kt",
],
static_libs: [
"WindowManager-Shell",
"junit",
"androidx.core_core-animation-testing",
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
"frameworks-base-testutils",
"mockito-kotlin2",
"mockito-target-extended-minus-junit4",
"truth",
"platform-test-annotations",
"platform-test-rules",
],
libs: [
"android.test.base",
"android.test.runner",
],
jni_libs: [
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
kotlincflags: ["-Xjvm-default=all"],
optimize: {
enabled: false,
},
test_suites: ["device-tests"],
platform_apis: true,
certificate: "platform",
aaptflags: [
"--extra-packages",
"com.android.wm.shell",
],
manifest: "AndroidManifest.xml",
}
@@ -1,13 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell.multivalenttests">
<application android:debuggable="true" android:supportsRtl="true" >
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:label="Multivalent tests for WindowManager-Shell"
android:targetPackage="com.android.wm.shell.multivalenttests">
</instrumentation>
</manifest>
@@ -1,3 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell.multivalenttests">
</manifest>
@@ -1,31 +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.
-->
<configuration description="Runs Tests for WindowManagerShellLib">
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
<option name="test-file-name" value="WMShellMultivalentTestsOnDevice.apk" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="framework-base-presubmit" />
<option name="test-tag" value="WMShellMultivalentTestsOnDevice" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.wm.shell.multivalenttests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
-4
View File
@@ -1,4 +0,0 @@
atsjenk@google.com
liranb@google.com
madym@google.com
@@ -1,2 +0,0 @@
sdk=NEWEST_SDK
@@ -1,670 +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.bubbles
import android.content.Context
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.content.res.Resources
import android.graphics.Insets
import android.graphics.PointF
import android.graphics.Rect
import android.os.UserHandle
import android.view.WindowManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
/** Tests operations and the resulting state managed by [BubblePositioner]. */
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubblePositionerTest {
private lateinit var positioner: BubblePositioner
private val context = ApplicationProvider.getApplicationContext<Context>()
private val resources: Resources
get() = context.resources
private val defaultDeviceConfig =
DeviceConfig(
windowBounds = Rect(0, 0, 1000, 2000),
isLargeScreen = false,
isSmallTablet = false,
isLandscape = false,
isRtl = false,
insets = Insets.of(0, 0, 0, 0)
)
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
val windowManager = context.getSystemService(WindowManager::class.java)
positioner = BubblePositioner(context, windowManager)
}
@Test
fun testUpdate() {
val insets = Insets.of(10, 20, 5, 15)
val screenBounds = Rect(0, 0, 1000, 1200)
val availableRect = Rect(screenBounds)
availableRect.inset(insets)
positioner.update(defaultDeviceConfig.copy(insets = insets, windowBounds = screenBounds))
assertThat(positioner.availableRect).isEqualTo(availableRect)
assertThat(positioner.isLandscape).isFalse()
assertThat(positioner.isLargeScreen).isFalse()
assertThat(positioner.insets).isEqualTo(insets)
}
@Test
fun testShowBubblesVertically_phonePortrait() {
positioner.update(defaultDeviceConfig)
assertThat(positioner.showBubblesVertically()).isFalse()
}
@Test
fun testShowBubblesVertically_phoneLandscape() {
positioner.update(defaultDeviceConfig.copy(isLandscape = true))
assertThat(positioner.isLandscape).isTrue()
assertThat(positioner.showBubblesVertically()).isTrue()
}
@Test
fun testShowBubblesVertically_tablet() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
assertThat(positioner.showBubblesVertically()).isTrue()
}
/** If a resting position hasn't been set, calling it will return the default position. */
@Test
fun testGetRestingPosition_returnsDefaultPosition() {
positioner.update(defaultDeviceConfig)
val restingPosition = positioner.getRestingPosition()
val defaultPosition = positioner.defaultStartPosition
assertThat(restingPosition).isEqualTo(defaultPosition)
}
/** If a resting position has been set, it'll return that instead of the default position. */
@Test
fun testGetRestingPosition_returnsRestingPosition() {
positioner.update(defaultDeviceConfig)
val restingPosition = PointF(100f, 100f)
positioner.restingPosition = restingPosition
assertThat(positioner.getRestingPosition()).isEqualTo(restingPosition)
}
/** Test that the default resting position on phone is in upper left. */
@Test
fun testGetRestingPosition_bubble_onPhone() {
positioner.update(defaultDeviceConfig)
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val restingPosition = positioner.getRestingPosition()
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left)
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
}
@Test
fun testGetRestingPosition_bubble_onPhone_RTL() {
positioner.update(defaultDeviceConfig.copy(isRtl = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val restingPosition = positioner.getRestingPosition()
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right)
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
}
/** Test that the default resting position on tablet is middle left. */
@Test
fun testGetRestingPosition_chatBubble_onTablet() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val restingPosition = positioner.getRestingPosition()
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left)
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
}
@Test
fun testGetRestingPosition_chatBubble_onTablet_RTL() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val restingPosition = positioner.getRestingPosition()
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right)
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
}
/** Test that the default resting position on tablet is middle right. */
@Test
fun testGetDefaultPosition_appBubble_onTablet() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */)
assertThat(startPosition.x).isEqualTo(allowableStackRegion.right)
assertThat(startPosition.y).isEqualTo(defaultYPosition)
}
@Test
fun testGetRestingPosition_appBubble_onTablet_RTL() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */)
assertThat(startPosition.x).isEqualTo(allowableStackRegion.left)
assertThat(startPosition.y).isEqualTo(defaultYPosition)
}
@Test
fun testGetRestingPosition_afterBoundsChange() {
positioner.update(
defaultDeviceConfig.copy(isLargeScreen = true, windowBounds = Rect(0, 0, 2000, 1600))
)
// Set the resting position to the right side
var allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val restingPosition = PointF(allowableStackRegion.right, allowableStackRegion.centerY())
positioner.restingPosition = restingPosition
// Now make the device smaller
positioner.update(
defaultDeviceConfig.copy(isLargeScreen = false, windowBounds = Rect(0, 0, 1000, 1600))
)
// Check the resting position is on the correct side
allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
assertThat(positioner.restingPosition.x).isEqualTo(allowableStackRegion.right)
}
@Test
fun testHasUserModifiedDefaultPosition_false() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
positioner.restingPosition = positioner.defaultStartPosition
assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
}
@Test
fun testHasUserModifiedDefaultPosition_true() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
positioner.restingPosition = PointF(0f, 100f)
assertThat(positioner.hasUserModifiedDefaultPosition()).isTrue()
}
@Test
fun testBubbleBarExpandedViewHeightAndWidth() {
val deviceConfig =
defaultDeviceConfig.copy(
// portrait orientation
isLandscape = false,
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.setShowingInBubbleBar(true)
positioner.update(deviceConfig)
positioner.bubbleBarTopOnScreen = 2500
val spaceBetweenTopInsetAndBubbleBarInLandscape = 1680
val expandedViewVerticalSpacing =
resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
val expectedHeight =
spaceBetweenTopInsetAndBubbleBarInLandscape - 2 * expandedViewVerticalSpacing
val expectedWidth = resources.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width)
assertThat(positioner.getExpandedViewWidthForBubbleBar(false)).isEqualTo(expectedWidth)
assertThat(positioner.getExpandedViewHeightForBubbleBar(false)).isEqualTo(expectedHeight)
}
@Test
fun testBubbleBarExpandedViewHeightAndWidth_screenWidthTooSmall() {
val screenWidth = 300
val deviceConfig =
defaultDeviceConfig.copy(
// portrait orientation
isLandscape = false,
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, screenWidth, 2600)
)
positioner.setShowingInBubbleBar(true)
positioner.update(deviceConfig)
positioner.bubbleBarTopOnScreen = 2500
val spaceBetweenTopInsetAndBubbleBarInLandscape = 180
val expandedViewSpacing =
resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
val expectedHeight = spaceBetweenTopInsetAndBubbleBarInLandscape - 2 * expandedViewSpacing
val expectedWidth = screenWidth - 15 /* horizontal insets */ - 2 * expandedViewSpacing
assertThat(positioner.getExpandedViewWidthForBubbleBar(false)).isEqualTo(expectedWidth)
assertThat(positioner.getExpandedViewHeightForBubbleBar(false)).isEqualTo(expectedHeight)
}
@Test
fun testGetExpandedViewHeight_max() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT)
}
@Test
fun testGetExpandedViewHeight_customHeight_valid() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val minHeight =
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height)
val bubble =
Bubble(
"key",
ShortcutInfo.Builder(context, "id").build(),
minHeight + 100 /* desiredHeight */,
0 /* desiredHeightResId */,
"title",
0 /* taskId */,
null /* locus */,
true /* isDismissable */,
directExecutor()
) {}
// Ensure the height is the same as the desired value
assertThat(positioner.getExpandedViewHeight(bubble))
.isEqualTo(bubble.getDesiredHeight(context))
}
@Test
fun testGetExpandedViewHeight_customHeight_tooSmall() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val bubble =
Bubble(
"key",
ShortcutInfo.Builder(context, "id").build(),
10 /* desiredHeight */,
0 /* desiredHeightResId */,
"title",
0 /* taskId */,
null /* locus */,
true /* isDismissable */,
directExecutor()
) {}
// Ensure the height is the same as the desired value
val minHeight =
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height)
assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight)
}
@Test
fun testGetMaxExpandedViewHeight_onLargeTablet() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val manageButtonHeight =
context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height)
val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width)
val expandedViewPadding =
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
val expectedHeight =
1800 - 2 * 20 - manageButtonHeight - pointerWidth - expandedViewPadding * 2
assertThat(positioner.getMaxExpandedViewHeight(false /* isOverflow */))
.isEqualTo(expectedHeight)
}
@Test
fun testAreBubblesBottomAligned_largeScreen_true() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
assertThat(positioner.areBubblesBottomAligned()).isTrue()
}
@Test
fun testAreBubblesBottomAligned_largeScreen_landscape_false() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
isLandscape = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
assertThat(positioner.areBubblesBottomAligned()).isFalse()
}
@Test
fun testAreBubblesBottomAligned_smallTablet_false() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
isSmallTablet = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
assertThat(positioner.areBubblesBottomAligned()).isFalse()
}
@Test
fun testAreBubblesBottomAligned_phone_false() {
val deviceConfig =
defaultDeviceConfig.copy(
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
assertThat(positioner.areBubblesBottomAligned()).isFalse()
}
@Test
fun testExpandedViewY_phoneLandscape() {
val deviceConfig =
defaultDeviceConfig.copy(
isLandscape = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
// This bubble will have max height so it'll always be top aligned
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(positioner.getExpandedViewYTopAligned())
}
@Test
fun testExpandedViewY_phonePortrait() {
val deviceConfig =
defaultDeviceConfig.copy(
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
// Always top aligned in phone portrait
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(positioner.getExpandedViewYTopAligned())
}
@Test
fun testExpandedViewY_smallTabletLandscape() {
val deviceConfig =
defaultDeviceConfig.copy(
isSmallTablet = true,
isLandscape = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
// This bubble will have max height which is always top aligned on small tablets
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(positioner.getExpandedViewYTopAligned())
}
@Test
fun testExpandedViewY_smallTabletPortrait() {
val deviceConfig =
defaultDeviceConfig.copy(
isSmallTablet = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
// This bubble will have max height which is always top aligned on small tablets
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(positioner.getExpandedViewYTopAligned())
}
@Test
fun testExpandedViewY_largeScreenLandscape() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
isLandscape = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
// This bubble will have max height which is always top aligned on landscape, large tablet
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(positioner.getExpandedViewYTopAligned())
}
@Test
fun testExpandedViewY_largeScreenPortrait() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
val manageButtonHeight =
context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height)
val manageButtonPlusMargin =
manageButtonHeight +
2 * context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_margin)
val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width)
val expectedExpandedViewY =
positioner.availableRect.bottom -
manageButtonPlusMargin -
positioner.getExpandedViewHeightForLargeScreen() -
pointerWidth
// Bubbles are bottom aligned on portrait, large tablet
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(expectedExpandedViewY)
}
@Test
fun testGetTaskViewContentWidth_onLeft() {
positioner.update(defaultDeviceConfig.copy(insets = Insets.of(100, 0, 200, 0)))
val taskViewWidth = positioner.getTaskViewContentWidth(true /* onLeft */)
val paddings =
positioner.getExpandedViewContainerPadding(true /* onLeft */, false /* isOverflow */)
assertThat(taskViewWidth)
.isEqualTo(positioner.screenRect.width() - paddings[0] - paddings[2])
}
@Test
fun testGetTaskViewContentWidth_onRight() {
positioner.update(defaultDeviceConfig.copy(insets = Insets.of(100, 0, 200, 0)))
val taskViewWidth = positioner.getTaskViewContentWidth(false /* onLeft */)
val paddings =
positioner.getExpandedViewContainerPadding(false /* onLeft */, false /* isOverflow */)
assertThat(taskViewWidth)
.isEqualTo(positioner.screenRect.width() - paddings[0] - paddings[2])
}
@Test
fun testIsBubbleBarOnLeft_defaultsToRight() {
positioner.bubbleBarLocation = BubbleBarLocation.DEFAULT
assertThat(positioner.isBubbleBarOnLeft).isFalse()
// Check that left and right return expected position
positioner.bubbleBarLocation = BubbleBarLocation.LEFT
assertThat(positioner.isBubbleBarOnLeft).isTrue()
positioner.bubbleBarLocation = BubbleBarLocation.RIGHT
assertThat(positioner.isBubbleBarOnLeft).isFalse()
}
@Test
fun testIsBubbleBarOnLeft_rtlEnabled_defaultsToLeft() {
positioner.update(defaultDeviceConfig.copy(isRtl = true))
positioner.bubbleBarLocation = BubbleBarLocation.DEFAULT
assertThat(positioner.isBubbleBarOnLeft).isTrue()
// Check that left and right return expected position
positioner.bubbleBarLocation = BubbleBarLocation.LEFT
assertThat(positioner.isBubbleBarOnLeft).isTrue()
positioner.bubbleBarLocation = BubbleBarLocation.RIGHT
assertThat(positioner.isBubbleBarOnLeft).isFalse()
}
@Test
fun testGetBubbleBarExpandedViewBounds_onLeft() {
testGetBubbleBarExpandedViewBounds(onLeft = true, isOverflow = false)
}
@Test
fun testGetBubbleBarExpandedViewBounds_onRight() {
testGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = false)
}
@Test
fun testGetBubbleBarExpandedViewBounds_isOverflow_onLeft() {
testGetBubbleBarExpandedViewBounds(onLeft = true, isOverflow = true)
}
@Test
fun testGetBubbleBarExpandedViewBounds_isOverflow_onRight() {
testGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = true)
}
private fun testGetBubbleBarExpandedViewBounds(onLeft: Boolean, isOverflow: Boolean) {
positioner.setShowingInBubbleBar(true)
val windowBounds = Rect(0, 0, 2000, 2600)
val insets = Insets.of(10, 20, 5, 15)
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
isLandscape = true,
insets = insets,
windowBounds = windowBounds
)
positioner.update(deviceConfig)
val bubbleBarHeight = 100
positioner.bubbleBarTopOnScreen = windowBounds.bottom - insets.bottom - bubbleBarHeight
val expandedViewPadding =
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
val left: Int
val right: Int
if (onLeft) {
// Pin to the left, calculate right
left = deviceConfig.insets.left + expandedViewPadding
right = left + positioner.getExpandedViewWidthForBubbleBar(isOverflow)
} else {
// Pin to the right, calculate left
right =
deviceConfig.windowBounds.right - deviceConfig.insets.right - expandedViewPadding
left = right - positioner.getExpandedViewWidthForBubbleBar(isOverflow)
}
// Above the bubble bar
val bottom = positioner.bubbleBarTopOnScreen - expandedViewPadding
// Calculate right and top based on size
val top = bottom - positioner.getExpandedViewHeightForBubbleBar(isOverflow)
val expectedBounds = Rect(left, top, right, bottom)
val bounds = Rect()
positioner.getBubbleBarExpandedViewBounds(onLeft, isOverflow, bounds)
assertThat(bounds).isEqualTo(expectedBounds)
}
private val defaultYPosition: Float
/**
* Calculates the Y position bubbles should be placed based on the config. Based on the
* calculations in [BubblePositioner.getDefaultStartPosition] and
* [BubbleStackView.RelativeStackPosition].
*/
get() {
val isTablet = positioner.isLargeScreen
// On tablet the position is centered, on phone it is an offset from the top.
val desiredY =
if (isTablet) {
positioner.screenRect.height() / 2f - positioner.bubbleSize / 2f
} else {
context.resources
.getDimensionPixelOffset(R.dimen.bubble_stack_starting_offset_y)
.toFloat()
}
// Since we're visually centering the bubbles on tablet, use total screen height rather
// than the available height.
val height =
if (isTablet) {
positioner.screenRect.height()
} else {
positioner.availableRect.height()
}
val offsetPercent = (desiredY / height).coerceIn(0f, 1f)
val allowableStackRegion =
positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent
}
}
@@ -1,462 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.bubbles
import android.content.Context
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Icon
import android.os.UserHandle
import android.platform.test.flag.junit.SetFlagsRule
import android.view.IWindowManager
import android.view.WindowManager
import android.view.WindowManagerGlobal
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.common.ProtoLog
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.Flags
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
import com.android.wm.shell.common.FloatingContentCoordinator
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
/** Unit tests for [BubbleStackView]. */
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleStackViewTest {
@get:Rule val setFlagsRule = SetFlagsRule()
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var positioner: BubblePositioner
private lateinit var iconFactory: BubbleIconFactory
private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
private lateinit var bubbleStackView: BubbleStackView
private lateinit var shellExecutor: ShellExecutor
private lateinit var windowManager: IWindowManager
private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory
private lateinit var bubbleData: BubbleData
private lateinit var bubbleStackViewManager: FakeBubbleStackViewManager
private var sysuiProxy = mock<SysuiProxy>()
@Before
fun setUp() {
PhysicsAnimatorTestUtils.prepareForTest()
// Disable protolog tool when running the tests from studio
ProtoLog.REQUIRE_PROTOLOGTOOL = false
windowManager = WindowManagerGlobal.getWindowManagerService()!!
shellExecutor = TestShellExecutor()
val windowManager = context.getSystemService(WindowManager::class.java)
iconFactory =
BubbleIconFactory(
context,
context.resources.getDimensionPixelSize(R.dimen.bubble_size),
context.resources.getDimensionPixelSize(R.dimen.bubble_badge_size),
Color.BLACK,
context.resources.getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_stroke_width
)
)
positioner = BubblePositioner(context, windowManager)
bubbleData =
BubbleData(
context,
BubbleLogger(UiEventLoggerFake()),
positioner,
BubbleEducationController(context),
shellExecutor
)
bubbleStackViewManager = FakeBubbleStackViewManager()
expandedViewManager = FakeBubbleExpandedViewManager()
bubbleTaskViewFactory = FakeBubbleTaskViewFactory()
bubbleStackView =
BubbleStackView(
context,
bubbleStackViewManager,
positioner,
bubbleData,
null,
FloatingContentCoordinator(),
{ sysuiProxy },
shellExecutor
)
context
.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
.edit()
.putBoolean(StackEducationView.PREF_STACK_EDUCATION, true)
.apply()
}
@After
fun tearDown() {
PhysicsAnimatorTestUtils.tearDown()
}
@Test
fun addBubble() {
val bubble = createAndInflateBubble()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
}
@Test
fun tapBubbleToExpand() {
val bubble = createAndInflateBubble()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
var lastUpdate: BubbleData.Update? = null
val semaphore = Semaphore(0)
val listener =
BubbleData.Listener { update ->
lastUpdate = update
semaphore.release()
}
bubbleData.setListener(listener)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubble.iconView!!.performClick()
// we're checking the expanded state in BubbleData because that's the source of truth.
// This will eventually propagate an update back to the stack view, but setting the
// entire pipeline is outside the scope of a unit test.
assertThat(bubbleData.isExpanded).isTrue()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(lastUpdate).isNotNull()
assertThat(lastUpdate!!.expandedChanged).isTrue()
assertThat(lastUpdate!!.expanded).isTrue()
}
@Test
fun tapDifferentBubble_shouldReorder() {
val bubble1 = createAndInflateChatBubble(key = "bubble1")
val bubble2 = createAndInflateChatBubble(key = "bubble2")
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble1)
bubbleStackView.addBubble(bubble2)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(2)
assertThat(bubbleData.bubbles).hasSize(2)
assertThat(bubbleData.selectedBubble).isEqualTo(bubble2)
assertThat(bubble2.iconView).isNotNull()
var lastUpdate: BubbleData.Update? = null
val semaphore = Semaphore(0)
val listener =
BubbleData.Listener { update ->
lastUpdate = update
semaphore.release()
}
bubbleData.setListener(listener)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubble2.iconView!!.performClick()
assertThat(bubbleData.isExpanded).isTrue()
bubbleStackView.setSelectedBubble(bubble2)
bubbleStackView.isExpanded = true
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(lastUpdate!!.expanded).isTrue()
assertThat(lastUpdate!!.bubbles.map { it.key })
.containsExactly("bubble2", "bubble1")
.inOrder()
// wait for idle to allow the animation to start
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
// wait for the expansion animation to complete before interacting with the bubbles
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
AnimatableScaleMatrix.SCALE_X, AnimatableScaleMatrix.SCALE_Y)
// tap on bubble1 to select it
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubble1.iconView!!.performClick()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
// tap on bubble1 again to collapse the stack
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// we have to set the selected bubble in the stack view manually because we don't have a
// listener wired up.
bubbleStackView.setSelectedBubble(bubble1)
bubble1.iconView!!.performClick()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
assertThat(bubbleData.isExpanded).isFalse()
assertThat(lastUpdate!!.orderChanged).isTrue()
assertThat(lastUpdate!!.bubbles.map { it.key })
.containsExactly("bubble1", "bubble2")
.inOrder()
}
@EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun testCreateStackView_noOverflowContents_noOverflow() {
bubbleStackView =
BubbleStackView(
context,
bubbleStackViewManager,
positioner,
bubbleData,
null,
FloatingContentCoordinator(),
{ sysuiProxy },
shellExecutor
)
assertThat(bubbleData.overflowBubbles).isEmpty()
val bubbleOverflow = bubbleData.overflow
// Overflow shouldn't be attached
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isEqualTo(-1)
}
@EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun testCreateStackView_hasOverflowContents_hasOverflow() {
// Add a bubble to the overflow
val bubble1 = createAndInflateChatBubble(key = "bubble1")
bubbleData.notificationEntryUpdated(bubble1, false, false)
bubbleData.dismissBubbleWithKey(bubble1.key, Bubbles.DISMISS_USER_GESTURE)
assertThat(bubbleData.overflowBubbles).isNotEmpty()
bubbleStackView =
BubbleStackView(
context,
bubbleStackViewManager,
positioner,
bubbleData,
null,
FloatingContentCoordinator(),
{ sysuiProxy },
shellExecutor
)
val bubbleOverflow = bubbleData.overflow
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
}
@DisableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun testCreateStackView_noOverflowContents_hasOverflow() {
bubbleStackView =
BubbleStackView(
context,
bubbleStackViewManager,
positioner,
bubbleData,
null,
FloatingContentCoordinator(),
{ sysuiProxy },
shellExecutor
)
assertThat(bubbleData.overflowBubbles).isEmpty()
val bubbleOverflow = bubbleData.overflow
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
}
@EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun showOverflow_true() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.showOverflow(true)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
val bubbleOverflow = bubbleData.overflow
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
}
@EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun showOverflow_false() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.showOverflow(true)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
val bubbleOverflow = bubbleData.overflow
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.showOverflow(false)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
// The overflow should've been removed
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isEqualTo(-1)
}
@DisableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun showOverflow_ignored() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.showOverflow(false)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
// showOverflow should've been ignored, so the overflow would be attached
val bubbleOverflow = bubbleData.overflow
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
}
private fun createAndInflateChatBubble(key: String): Bubble {
val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build()
val bubble =
Bubble(
key,
shortcutInfo,
/* desiredHeight= */ 6,
Resources.ID_NULL,
"title",
/* taskId= */ 0,
"locus",
/* isDismissable= */ true,
directExecutor()
) {}
inflateBubble(bubble)
return bubble
}
private fun createAndInflateBubble(): Bubble {
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor())
inflateBubble(bubble)
return bubble
}
private fun inflateBubble(bubble: Bubble) {
bubble.setInflateSynchronously(true)
bubbleData.notificationEntryUpdated(bubble, true, false)
val semaphore = Semaphore(0)
val callback: BubbleViewInfoTask.Callback =
BubbleViewInfoTask.Callback { semaphore.release() }
bubble.inflate(
callback,
context,
expandedViewManager,
bubbleTaskViewFactory,
positioner,
bubbleStackView,
null,
iconFactory,
false
)
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubble.isInflated).isTrue()
}
private class FakeBubbleStackViewManager : BubbleStackViewManager {
override fun onAllBubblesAnimatedOut() {}
override fun updateWindowFlagsForBackpress(interceptBack: Boolean) {}
override fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>) {}
override fun hideCurrentInputMethod() {}
}
private class TestShellExecutor : ShellExecutor {
override fun execute(runnable: Runnable) {
runnable.run()
}
override fun executeDelayed(r: Runnable, delayMillis: Long) {
r.run()
}
override fun removeCallbacks(r: Runnable?) {}
override fun hasCallback(r: Runnable): Boolean = false
}
private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
override fun create(): BubbleTaskView {
val taskViewTaskController = mock<TaskViewTaskController>()
val taskView = TaskView(context, taskViewTaskController)
return BubbleTaskView(taskView, shellExecutor)
}
}
private inner class FakeBubbleExpandedViewManager : BubbleExpandedViewManager {
override val overflowBubbles: List<Bubble>
get() = emptyList()
override fun setOverflowListener(listener: BubbleData.Listener) {}
override fun collapseStack() {}
override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
override fun promoteBubbleFromOverflow(bubble: Bubble) {}
override fun removeBubble(key: String, reason: Int) {}
override fun dismissBubble(bubble: Bubble, reason: Int) {}
override fun setAppBubbleTaskId(key: String, taskId: Int) {}
override fun isStackExpanded(): Boolean = false
override fun isShowingAsBubbleBar(): Boolean = false
override fun hideCurrentInputMethod() {}
}
}
@@ -1,92 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.bubbles
import android.content.ComponentName
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.wm.shell.taskview.TaskView
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleTaskViewTest {
private lateinit var bubbleTaskView: BubbleTaskView
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var taskView: TaskView
@Before
fun setUp() {
taskView = mock()
bubbleTaskView = BubbleTaskView(taskView, directExecutor())
}
@Test
fun onTaskCreated_updatesState() {
val componentName = ComponentName(context, "TestClass")
bubbleTaskView.listener.onTaskCreated(123, componentName)
assertThat(bubbleTaskView.taskId).isEqualTo(123)
assertThat(bubbleTaskView.componentName).isEqualTo(componentName)
assertThat(bubbleTaskView.isCreated).isTrue()
}
@Test
fun onTaskCreated_callsDelegateListener() {
var actualTaskId = -1
var actualComponentName: ComponentName? = null
val delegateListener = object : TaskView.Listener {
override fun onTaskCreated(taskId: Int, name: ComponentName) {
actualTaskId = taskId
actualComponentName = name
}
}
bubbleTaskView.delegateListener = delegateListener
val componentName = ComponentName(context, "TestClass")
bubbleTaskView.listener.onTaskCreated(123, componentName)
assertThat(actualTaskId).isEqualTo(123)
assertThat(actualComponentName).isEqualTo(componentName)
}
@Test
fun cleanup_invalidTaskId_doesNotRemoveTask() {
bubbleTaskView.cleanup()
verify(taskView, never()).removeTask()
}
@Test
fun cleanup_validTaskId_removesTask() {
val componentName = ComponentName(context, "TestClass")
bubbleTaskView.listener.onTaskCreated(123, componentName)
bubbleTaskView.cleanup()
verify(taskView).removeTask()
}
}
@@ -1,459 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.bubbles.bar
import android.content.Context
import android.graphics.Insets
import android.graphics.PointF
import android.graphics.Rect
import android.view.View
import android.view.WindowManager
import android.widget.FrameLayout
import androidx.core.animation.AnimatorTestRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.bubbles.DeviceConfig
import com.android.wm.shell.common.bubbles.BaseBubblePinController
import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.ClassRule
import org.junit.Test
import org.junit.runner.RunWith
/** Tests for [BubbleExpandedViewPinController] */
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleExpandedViewPinControllerTest {
companion object {
@JvmField @ClassRule val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
const val SCREEN_WIDTH = 2000
const val SCREEN_HEIGHT = 1000
const val BUBBLE_BAR_HEIGHT = 50
}
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var positioner: BubblePositioner
private lateinit var container: FrameLayout
private lateinit var controller: BubbleExpandedViewPinController
private lateinit var testListener: TestLocationChangeListener
private val dropTargetView: View?
get() = container.findViewById(R.id.bubble_bar_drop_target)
private val pointOnLeft = PointF(100f, 100f)
private val pointOnRight = PointF(1900f, 500f)
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
container = FrameLayout(context)
val windowManager = context.getSystemService(WindowManager::class.java)
positioner = BubblePositioner(context, windowManager)
positioner.setShowingInBubbleBar(true)
val deviceConfig =
DeviceConfig(
windowBounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
isLargeScreen = true,
isSmallTablet = false,
isLandscape = true,
isRtl = false,
insets = Insets.of(10, 20, 30, 40)
)
positioner.update(deviceConfig)
positioner.bubbleBarTopOnScreen =
SCREEN_HEIGHT - deviceConfig.insets.bottom - BUBBLE_BAR_HEIGHT
controller = BubbleExpandedViewPinController(context, container, positioner)
testListener = TestLocationChangeListener()
controller.setListener(testListener)
}
@After
fun tearDown() {
getInstrumentation().runOnMainSync { controller.onDragEnd() }
waitForAnimateOut()
}
/** Dragging on same side should not show drop target or trigger location changes */
@Test
fun drag_stayOnRightSide() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onDragEnd()
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).containsExactly(RIGHT)
}
/** Dragging on same side should not show drop target or trigger location changes */
@Test
fun drag_stayOnLeftSide() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onDragEnd()
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).containsExactly(LEFT)
}
/** Drag crosses to the other side. Show drop target and trigger a location change. */
@Test
fun drag_rightToLeft() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
assertThat(testListener.locationChanges).containsExactly(LEFT)
assertThat(testListener.locationReleases).isEmpty()
}
/** Drag crosses to the other side. Show drop target and trigger a location change. */
@Test
fun drag_leftToRight() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
assertThat(testListener.locationChanges).containsExactly(RIGHT)
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drop target does not initially show on the side that the drag starts. Check that it shows up
* after the dragging the view to other side and back to the initial side.
*/
@Test
fun drag_rightToLeftToRight() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
getInstrumentation().runOnMainSync { controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y) }
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
getInstrumentation().runOnMainSync {
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateOut()
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
assertThat(testListener.locationChanges).containsExactly(LEFT, RIGHT).inOrder()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drop target does not initially show on the side that the drag starts. Check that it shows up
* after the dragging the view to other side and back to the initial side.
*/
@Test
fun drag_leftToRightToLeft() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
getInstrumentation().runOnMainSync {
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
getInstrumentation().runOnMainSync { controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y) }
waitForAnimateOut()
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
assertThat(testListener.locationChanges).containsExactly(RIGHT, LEFT).inOrder()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag from right to left, but stay in exclusion rect around the dismiss view. Drop target
* should not show and location change should not trigger.
*/
@Test
fun drag_rightToLeft_inExclusionRect() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
// Exclusion rect is around the bottom center area of the screen
controller.onDragUpdate(SCREEN_WIDTH / 2f - 50, SCREEN_HEIGHT - 100f)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag from left to right, but stay in exclusion rect around the dismiss view. Drop target
* should not show and location change should not trigger.
*/
@Test
fun drag_leftToRight_inExclusionRect() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
// Exclusion rect is around the bottom center area of the screen
controller.onDragUpdate(SCREEN_WIDTH / 2f + 50, SCREEN_HEIGHT - 100f)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag to dismiss target and back to the same side should not cause the drop target to show.
*/
@Test
fun drag_rightToDismissToRight() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onStuckToDismissTarget()
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag to dismiss target and back to the same side should not cause the drop target to show.
*/
@Test
fun drag_leftToDismissToLeft() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onStuckToDismissTarget()
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).isEmpty()
}
/** Drag to dismiss target and other side should show drop target on the other side. */
@Test
fun drag_rightToDismissToLeft() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onStuckToDismissTarget()
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
assertThat(testListener.locationChanges).containsExactly(LEFT)
assertThat(testListener.locationReleases).isEmpty()
}
/** Drag to dismiss target and other side should show drop target on the other side. */
@Test
fun drag_leftToDismissToRight() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onStuckToDismissTarget()
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
assertThat(testListener.locationChanges).containsExactly(RIGHT)
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag to dismiss should trigger a location change to the initial location, if the current
* location is different. And hide the drop target.
*/
@Test
fun drag_rightToLeftToDismiss() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
getInstrumentation().runOnMainSync { controller.onStuckToDismissTarget() }
waitForAnimateOut()
assertThat(dropTargetView!!.alpha).isEqualTo(0f)
assertThat(testListener.locationChanges).containsExactly(LEFT, RIGHT).inOrder()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag to dismiss should trigger a location change to the initial location, if the current
* location is different. And hide the drop target.
*/
@Test
fun drag_leftToRightToDismiss() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
getInstrumentation().runOnMainSync { controller.onStuckToDismissTarget() }
waitForAnimateOut()
assertThat(dropTargetView!!.alpha).isEqualTo(0f)
assertThat(testListener.locationChanges).containsExactly(RIGHT, LEFT).inOrder()
assertThat(testListener.locationReleases).isEmpty()
}
/** Finishing drag should remove drop target and send location update. */
@Test
fun drag_rightToLeftRelease() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
getInstrumentation().runOnMainSync { controller.onDragEnd() }
waitForAnimateOut()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).containsExactly(LEFT)
assertThat(testListener.locationReleases).containsExactly(LEFT)
}
/** Finishing drag should remove drop target and send location update. */
@Test
fun drag_leftToRightRelease() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
getInstrumentation().runOnMainSync { controller.onDragEnd() }
waitForAnimateOut()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).containsExactly(RIGHT)
assertThat(testListener.locationReleases).containsExactly(RIGHT)
}
private fun getExpectedDropTargetBoundsOnLeft(): Rect =
Rect().also {
positioner.getBubbleBarExpandedViewBounds(
true /* onLeft */,
false /* isOverflowExpanded */,
it
)
}
private fun getExpectedDropTargetBoundsOnRight(): Rect =
Rect().also {
positioner.getBubbleBarExpandedViewBounds(
false /* onLeft */,
false /* isOverflowExpanded */,
it
)
}
private fun waitForAnimateIn() {
// Advance animator for on-device test
getInstrumentation().runOnMainSync {
animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_IN_DURATION)
}
}
private fun waitForAnimateOut() {
// Advance animator for on-device test
getInstrumentation().runOnMainSync {
animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_OUT_DURATION)
}
}
private fun View.bounds(): Rect {
return Rect(0, 0, layoutParams.width, layoutParams.height).also { rect ->
rect.offsetTo(x.toInt(), y.toInt())
}
}
internal class TestLocationChangeListener : BaseBubblePinController.LocationChangeListener {
val locationChanges = mutableListOf<BubbleBarLocation>()
val locationReleases = mutableListOf<BubbleBarLocation>()
override fun onChange(location: BubbleBarLocation) {
locationChanges.add(location)
}
override fun onRelease(location: BubbleBarLocation) {
locationReleases.add(location)
}
}
}
@@ -1,99 +0,0 @@
// Copyright (C) 2019 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
default_team: "trendy_team_multitasking_windowing",
}
android_app {
name: "WindowManagerShellRobolectric",
platform_apis: true,
static_libs: [
"WindowManager-Shell",
],
manifest: "AndroidManifestRobolectric.xml",
use_resource_processor: true,
}
android_robolectric_test {
name: "WMShellRobolectricTests",
instrumentation_for: "WindowManagerShellRobolectric",
upstream: true,
java_resource_dirs: [
"robolectric/config",
],
srcs: [
"src/**/*.kt",
],
// TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed.
exclude_srcs: ["src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"],
static_libs: [
"junit",
"androidx.core_core-animation-testing",
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
"mockito-robolectric-prebuilt",
"mockito-kotlin2",
"truth",
],
auto_gen_config: true,
}
android_test {
name: "WMShellMultivalentTestsOnDevice",
srcs: [
"src/**/*.kt",
],
static_libs: [
"WindowManager-Shell",
"junit",
"androidx.core_core-animation-testing",
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
"frameworks-base-testutils",
"mockito-kotlin2",
"mockito-target-extended-minus-junit4",
"truth",
"platform-test-annotations",
"platform-test-rules",
],
libs: [
"android.test.base",
"android.test.runner",
],
jni_libs: [
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
kotlincflags: ["-Xjvm-default=all"],
optimize: {
enabled: false,
},
test_suites: ["device-tests"],
platform_apis: true,
certificate: "platform",
aaptflags: [
"--extra-packages",
"com.android.wm.shell",
],
manifest: "AndroidManifest.xml",
}
@@ -1,13 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell.multivalenttests">
<application android:debuggable="true" android:supportsRtl="true" >
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:label="Multivalent tests for WindowManager-Shell"
android:targetPackage="com.android.wm.shell.multivalenttests">
</instrumentation>
</manifest>
@@ -1,3 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell.multivalenttests">
</manifest>
@@ -1,31 +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.
-->
<configuration description="Runs Tests for WindowManagerShellLib">
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
<option name="test-file-name" value="WMShellMultivalentTestsOnDevice.apk" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="framework-base-presubmit" />
<option name="test-tag" value="WMShellMultivalentTestsOnDevice" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.wm.shell.multivalenttests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
@@ -1,4 +0,0 @@
atsjenk@google.com
liranb@google.com
madym@google.com
@@ -1,2 +0,0 @@
sdk=NEWEST_SDK
@@ -1,670 +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.bubbles
import android.content.Context
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.content.res.Resources
import android.graphics.Insets
import android.graphics.PointF
import android.graphics.Rect
import android.os.UserHandle
import android.view.WindowManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
/** Tests operations and the resulting state managed by [BubblePositioner]. */
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubblePositionerTest {
private lateinit var positioner: BubblePositioner
private val context = ApplicationProvider.getApplicationContext<Context>()
private val resources: Resources
get() = context.resources
private val defaultDeviceConfig =
DeviceConfig(
windowBounds = Rect(0, 0, 1000, 2000),
isLargeScreen = false,
isSmallTablet = false,
isLandscape = false,
isRtl = false,
insets = Insets.of(0, 0, 0, 0)
)
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
val windowManager = context.getSystemService(WindowManager::class.java)
positioner = BubblePositioner(context, windowManager)
}
@Test
fun testUpdate() {
val insets = Insets.of(10, 20, 5, 15)
val screenBounds = Rect(0, 0, 1000, 1200)
val availableRect = Rect(screenBounds)
availableRect.inset(insets)
positioner.update(defaultDeviceConfig.copy(insets = insets, windowBounds = screenBounds))
assertThat(positioner.availableRect).isEqualTo(availableRect)
assertThat(positioner.isLandscape).isFalse()
assertThat(positioner.isLargeScreen).isFalse()
assertThat(positioner.insets).isEqualTo(insets)
}
@Test
fun testShowBubblesVertically_phonePortrait() {
positioner.update(defaultDeviceConfig)
assertThat(positioner.showBubblesVertically()).isFalse()
}
@Test
fun testShowBubblesVertically_phoneLandscape() {
positioner.update(defaultDeviceConfig.copy(isLandscape = true))
assertThat(positioner.isLandscape).isTrue()
assertThat(positioner.showBubblesVertically()).isTrue()
}
@Test
fun testShowBubblesVertically_tablet() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
assertThat(positioner.showBubblesVertically()).isTrue()
}
/** If a resting position hasn't been set, calling it will return the default position. */
@Test
fun testGetRestingPosition_returnsDefaultPosition() {
positioner.update(defaultDeviceConfig)
val restingPosition = positioner.getRestingPosition()
val defaultPosition = positioner.defaultStartPosition
assertThat(restingPosition).isEqualTo(defaultPosition)
}
/** If a resting position has been set, it'll return that instead of the default position. */
@Test
fun testGetRestingPosition_returnsRestingPosition() {
positioner.update(defaultDeviceConfig)
val restingPosition = PointF(100f, 100f)
positioner.restingPosition = restingPosition
assertThat(positioner.getRestingPosition()).isEqualTo(restingPosition)
}
/** Test that the default resting position on phone is in upper left. */
@Test
fun testGetRestingPosition_bubble_onPhone() {
positioner.update(defaultDeviceConfig)
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val restingPosition = positioner.getRestingPosition()
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left)
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
}
@Test
fun testGetRestingPosition_bubble_onPhone_RTL() {
positioner.update(defaultDeviceConfig.copy(isRtl = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val restingPosition = positioner.getRestingPosition()
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right)
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
}
/** Test that the default resting position on tablet is middle left. */
@Test
fun testGetRestingPosition_chatBubble_onTablet() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val restingPosition = positioner.getRestingPosition()
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left)
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
}
@Test
fun testGetRestingPosition_chatBubble_onTablet_RTL() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val restingPosition = positioner.getRestingPosition()
assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right)
assertThat(restingPosition.y).isEqualTo(defaultYPosition)
}
/** Test that the default resting position on tablet is middle right. */
@Test
fun testGetDefaultPosition_appBubble_onTablet() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */)
assertThat(startPosition.x).isEqualTo(allowableStackRegion.right)
assertThat(startPosition.y).isEqualTo(defaultYPosition)
}
@Test
fun testGetRestingPosition_appBubble_onTablet_RTL() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */)
assertThat(startPosition.x).isEqualTo(allowableStackRegion.left)
assertThat(startPosition.y).isEqualTo(defaultYPosition)
}
@Test
fun testGetRestingPosition_afterBoundsChange() {
positioner.update(
defaultDeviceConfig.copy(isLargeScreen = true, windowBounds = Rect(0, 0, 2000, 1600))
)
// Set the resting position to the right side
var allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
val restingPosition = PointF(allowableStackRegion.right, allowableStackRegion.centerY())
positioner.restingPosition = restingPosition
// Now make the device smaller
positioner.update(
defaultDeviceConfig.copy(isLargeScreen = false, windowBounds = Rect(0, 0, 1000, 1600))
)
// Check the resting position is on the correct side
allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
assertThat(positioner.restingPosition.x).isEqualTo(allowableStackRegion.right)
}
@Test
fun testHasUserModifiedDefaultPosition_false() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
positioner.restingPosition = positioner.defaultStartPosition
assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
}
@Test
fun testHasUserModifiedDefaultPosition_true() {
positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true))
assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse()
positioner.restingPosition = PointF(0f, 100f)
assertThat(positioner.hasUserModifiedDefaultPosition()).isTrue()
}
@Test
fun testBubbleBarExpandedViewHeightAndWidth() {
val deviceConfig =
defaultDeviceConfig.copy(
// portrait orientation
isLandscape = false,
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.setShowingInBubbleBar(true)
positioner.update(deviceConfig)
positioner.bubbleBarTopOnScreen = 2500
val spaceBetweenTopInsetAndBubbleBarInLandscape = 1680
val expandedViewVerticalSpacing =
resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
val expectedHeight =
spaceBetweenTopInsetAndBubbleBarInLandscape - 2 * expandedViewVerticalSpacing
val expectedWidth = resources.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width)
assertThat(positioner.getExpandedViewWidthForBubbleBar(false)).isEqualTo(expectedWidth)
assertThat(positioner.getExpandedViewHeightForBubbleBar(false)).isEqualTo(expectedHeight)
}
@Test
fun testBubbleBarExpandedViewHeightAndWidth_screenWidthTooSmall() {
val screenWidth = 300
val deviceConfig =
defaultDeviceConfig.copy(
// portrait orientation
isLandscape = false,
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, screenWidth, 2600)
)
positioner.setShowingInBubbleBar(true)
positioner.update(deviceConfig)
positioner.bubbleBarTopOnScreen = 2500
val spaceBetweenTopInsetAndBubbleBarInLandscape = 180
val expandedViewSpacing =
resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
val expectedHeight = spaceBetweenTopInsetAndBubbleBarInLandscape - 2 * expandedViewSpacing
val expectedWidth = screenWidth - 15 /* horizontal insets */ - 2 * expandedViewSpacing
assertThat(positioner.getExpandedViewWidthForBubbleBar(false)).isEqualTo(expectedWidth)
assertThat(positioner.getExpandedViewHeightForBubbleBar(false)).isEqualTo(expectedHeight)
}
@Test
fun testGetExpandedViewHeight_max() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT)
}
@Test
fun testGetExpandedViewHeight_customHeight_valid() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val minHeight =
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height)
val bubble =
Bubble(
"key",
ShortcutInfo.Builder(context, "id").build(),
minHeight + 100 /* desiredHeight */,
0 /* desiredHeightResId */,
"title",
0 /* taskId */,
null /* locus */,
true /* isDismissable */,
directExecutor()
) {}
// Ensure the height is the same as the desired value
assertThat(positioner.getExpandedViewHeight(bubble))
.isEqualTo(bubble.getDesiredHeight(context))
}
@Test
fun testGetExpandedViewHeight_customHeight_tooSmall() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val bubble =
Bubble(
"key",
ShortcutInfo.Builder(context, "id").build(),
10 /* desiredHeight */,
0 /* desiredHeightResId */,
"title",
0 /* taskId */,
null /* locus */,
true /* isDismissable */,
directExecutor()
) {}
// Ensure the height is the same as the desired value
val minHeight =
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height)
assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight)
}
@Test
fun testGetMaxExpandedViewHeight_onLargeTablet() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val manageButtonHeight =
context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height)
val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width)
val expandedViewPadding =
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
val expectedHeight =
1800 - 2 * 20 - manageButtonHeight - pointerWidth - expandedViewPadding * 2
assertThat(positioner.getMaxExpandedViewHeight(false /* isOverflow */))
.isEqualTo(expectedHeight)
}
@Test
fun testAreBubblesBottomAligned_largeScreen_true() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
assertThat(positioner.areBubblesBottomAligned()).isTrue()
}
@Test
fun testAreBubblesBottomAligned_largeScreen_landscape_false() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
isLandscape = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
assertThat(positioner.areBubblesBottomAligned()).isFalse()
}
@Test
fun testAreBubblesBottomAligned_smallTablet_false() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
isSmallTablet = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
assertThat(positioner.areBubblesBottomAligned()).isFalse()
}
@Test
fun testAreBubblesBottomAligned_phone_false() {
val deviceConfig =
defaultDeviceConfig.copy(
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
assertThat(positioner.areBubblesBottomAligned()).isFalse()
}
@Test
fun testExpandedViewY_phoneLandscape() {
val deviceConfig =
defaultDeviceConfig.copy(
isLandscape = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
// This bubble will have max height so it'll always be top aligned
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(positioner.getExpandedViewYTopAligned())
}
@Test
fun testExpandedViewY_phonePortrait() {
val deviceConfig =
defaultDeviceConfig.copy(
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
// Always top aligned in phone portrait
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(positioner.getExpandedViewYTopAligned())
}
@Test
fun testExpandedViewY_smallTabletLandscape() {
val deviceConfig =
defaultDeviceConfig.copy(
isSmallTablet = true,
isLandscape = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
// This bubble will have max height which is always top aligned on small tablets
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(positioner.getExpandedViewYTopAligned())
}
@Test
fun testExpandedViewY_smallTabletPortrait() {
val deviceConfig =
defaultDeviceConfig.copy(
isSmallTablet = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
// This bubble will have max height which is always top aligned on small tablets
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(positioner.getExpandedViewYTopAligned())
}
@Test
fun testExpandedViewY_largeScreenLandscape() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
isLandscape = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
// This bubble will have max height which is always top aligned on landscape, large tablet
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(positioner.getExpandedViewYTopAligned())
}
@Test
fun testExpandedViewY_largeScreenPortrait() {
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
positioner.update(deviceConfig)
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
val manageButtonHeight =
context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height)
val manageButtonPlusMargin =
manageButtonHeight +
2 * context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_margin)
val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width)
val expectedExpandedViewY =
positioner.availableRect.bottom -
manageButtonPlusMargin -
positioner.getExpandedViewHeightForLargeScreen() -
pointerWidth
// Bubbles are bottom aligned on portrait, large tablet
assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
.isEqualTo(expectedExpandedViewY)
}
@Test
fun testGetTaskViewContentWidth_onLeft() {
positioner.update(defaultDeviceConfig.copy(insets = Insets.of(100, 0, 200, 0)))
val taskViewWidth = positioner.getTaskViewContentWidth(true /* onLeft */)
val paddings =
positioner.getExpandedViewContainerPadding(true /* onLeft */, false /* isOverflow */)
assertThat(taskViewWidth)
.isEqualTo(positioner.screenRect.width() - paddings[0] - paddings[2])
}
@Test
fun testGetTaskViewContentWidth_onRight() {
positioner.update(defaultDeviceConfig.copy(insets = Insets.of(100, 0, 200, 0)))
val taskViewWidth = positioner.getTaskViewContentWidth(false /* onLeft */)
val paddings =
positioner.getExpandedViewContainerPadding(false /* onLeft */, false /* isOverflow */)
assertThat(taskViewWidth)
.isEqualTo(positioner.screenRect.width() - paddings[0] - paddings[2])
}
@Test
fun testIsBubbleBarOnLeft_defaultsToRight() {
positioner.bubbleBarLocation = BubbleBarLocation.DEFAULT
assertThat(positioner.isBubbleBarOnLeft).isFalse()
// Check that left and right return expected position
positioner.bubbleBarLocation = BubbleBarLocation.LEFT
assertThat(positioner.isBubbleBarOnLeft).isTrue()
positioner.bubbleBarLocation = BubbleBarLocation.RIGHT
assertThat(positioner.isBubbleBarOnLeft).isFalse()
}
@Test
fun testIsBubbleBarOnLeft_rtlEnabled_defaultsToLeft() {
positioner.update(defaultDeviceConfig.copy(isRtl = true))
positioner.bubbleBarLocation = BubbleBarLocation.DEFAULT
assertThat(positioner.isBubbleBarOnLeft).isTrue()
// Check that left and right return expected position
positioner.bubbleBarLocation = BubbleBarLocation.LEFT
assertThat(positioner.isBubbleBarOnLeft).isTrue()
positioner.bubbleBarLocation = BubbleBarLocation.RIGHT
assertThat(positioner.isBubbleBarOnLeft).isFalse()
}
@Test
fun testGetBubbleBarExpandedViewBounds_onLeft() {
testGetBubbleBarExpandedViewBounds(onLeft = true, isOverflow = false)
}
@Test
fun testGetBubbleBarExpandedViewBounds_onRight() {
testGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = false)
}
@Test
fun testGetBubbleBarExpandedViewBounds_isOverflow_onLeft() {
testGetBubbleBarExpandedViewBounds(onLeft = true, isOverflow = true)
}
@Test
fun testGetBubbleBarExpandedViewBounds_isOverflow_onRight() {
testGetBubbleBarExpandedViewBounds(onLeft = false, isOverflow = true)
}
private fun testGetBubbleBarExpandedViewBounds(onLeft: Boolean, isOverflow: Boolean) {
positioner.setShowingInBubbleBar(true)
val windowBounds = Rect(0, 0, 2000, 2600)
val insets = Insets.of(10, 20, 5, 15)
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
isLandscape = true,
insets = insets,
windowBounds = windowBounds
)
positioner.update(deviceConfig)
val bubbleBarHeight = 100
positioner.bubbleBarTopOnScreen = windowBounds.bottom - insets.bottom - bubbleBarHeight
val expandedViewPadding =
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
val left: Int
val right: Int
if (onLeft) {
// Pin to the left, calculate right
left = deviceConfig.insets.left + expandedViewPadding
right = left + positioner.getExpandedViewWidthForBubbleBar(isOverflow)
} else {
// Pin to the right, calculate left
right =
deviceConfig.windowBounds.right - deviceConfig.insets.right - expandedViewPadding
left = right - positioner.getExpandedViewWidthForBubbleBar(isOverflow)
}
// Above the bubble bar
val bottom = positioner.bubbleBarTopOnScreen - expandedViewPadding
// Calculate right and top based on size
val top = bottom - positioner.getExpandedViewHeightForBubbleBar(isOverflow)
val expectedBounds = Rect(left, top, right, bottom)
val bounds = Rect()
positioner.getBubbleBarExpandedViewBounds(onLeft, isOverflow, bounds)
assertThat(bounds).isEqualTo(expectedBounds)
}
private val defaultYPosition: Float
/**
* Calculates the Y position bubbles should be placed based on the config. Based on the
* calculations in [BubblePositioner.getDefaultStartPosition] and
* [BubbleStackView.RelativeStackPosition].
*/
get() {
val isTablet = positioner.isLargeScreen
// On tablet the position is centered, on phone it is an offset from the top.
val desiredY =
if (isTablet) {
positioner.screenRect.height() / 2f - positioner.bubbleSize / 2f
} else {
context.resources
.getDimensionPixelOffset(R.dimen.bubble_stack_starting_offset_y)
.toFloat()
}
// Since we're visually centering the bubbles on tablet, use total screen height rather
// than the available height.
val height =
if (isTablet) {
positioner.screenRect.height()
} else {
positioner.availableRect.height()
}
val offsetPercent = (desiredY / height).coerceIn(0f, 1f)
val allowableStackRegion =
positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent
}
}
@@ -1,462 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.bubbles
import android.content.Context
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Icon
import android.os.UserHandle
import android.platform.test.flag.junit.SetFlagsRule
import android.view.IWindowManager
import android.view.WindowManager
import android.view.WindowManagerGlobal
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.common.ProtoLog
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.Flags
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
import com.android.wm.shell.common.FloatingContentCoordinator
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
/** Unit tests for [BubbleStackView]. */
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleStackViewTest {
@get:Rule val setFlagsRule = SetFlagsRule()
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var positioner: BubblePositioner
private lateinit var iconFactory: BubbleIconFactory
private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
private lateinit var bubbleStackView: BubbleStackView
private lateinit var shellExecutor: ShellExecutor
private lateinit var windowManager: IWindowManager
private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory
private lateinit var bubbleData: BubbleData
private lateinit var bubbleStackViewManager: FakeBubbleStackViewManager
private var sysuiProxy = mock<SysuiProxy>()
@Before
fun setUp() {
PhysicsAnimatorTestUtils.prepareForTest()
// Disable protolog tool when running the tests from studio
ProtoLog.REQUIRE_PROTOLOGTOOL = false
windowManager = WindowManagerGlobal.getWindowManagerService()!!
shellExecutor = TestShellExecutor()
val windowManager = context.getSystemService(WindowManager::class.java)
iconFactory =
BubbleIconFactory(
context,
context.resources.getDimensionPixelSize(R.dimen.bubble_size),
context.resources.getDimensionPixelSize(R.dimen.bubble_badge_size),
Color.BLACK,
context.resources.getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_stroke_width
)
)
positioner = BubblePositioner(context, windowManager)
bubbleData =
BubbleData(
context,
BubbleLogger(UiEventLoggerFake()),
positioner,
BubbleEducationController(context),
shellExecutor
)
bubbleStackViewManager = FakeBubbleStackViewManager()
expandedViewManager = FakeBubbleExpandedViewManager()
bubbleTaskViewFactory = FakeBubbleTaskViewFactory()
bubbleStackView =
BubbleStackView(
context,
bubbleStackViewManager,
positioner,
bubbleData,
null,
FloatingContentCoordinator(),
{ sysuiProxy },
shellExecutor
)
context
.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
.edit()
.putBoolean(StackEducationView.PREF_STACK_EDUCATION, true)
.apply()
}
@After
fun tearDown() {
PhysicsAnimatorTestUtils.tearDown()
}
@Test
fun addBubble() {
val bubble = createAndInflateBubble()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
}
@Test
fun tapBubbleToExpand() {
val bubble = createAndInflateBubble()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
var lastUpdate: BubbleData.Update? = null
val semaphore = Semaphore(0)
val listener =
BubbleData.Listener { update ->
lastUpdate = update
semaphore.release()
}
bubbleData.setListener(listener)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubble.iconView!!.performClick()
// we're checking the expanded state in BubbleData because that's the source of truth.
// This will eventually propagate an update back to the stack view, but setting the
// entire pipeline is outside the scope of a unit test.
assertThat(bubbleData.isExpanded).isTrue()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(lastUpdate).isNotNull()
assertThat(lastUpdate!!.expandedChanged).isTrue()
assertThat(lastUpdate!!.expanded).isTrue()
}
@Test
fun tapDifferentBubble_shouldReorder() {
val bubble1 = createAndInflateChatBubble(key = "bubble1")
val bubble2 = createAndInflateChatBubble(key = "bubble2")
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.addBubble(bubble1)
bubbleStackView.addBubble(bubble2)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(2)
assertThat(bubbleData.bubbles).hasSize(2)
assertThat(bubbleData.selectedBubble).isEqualTo(bubble2)
assertThat(bubble2.iconView).isNotNull()
var lastUpdate: BubbleData.Update? = null
val semaphore = Semaphore(0)
val listener =
BubbleData.Listener { update ->
lastUpdate = update
semaphore.release()
}
bubbleData.setListener(listener)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubble2.iconView!!.performClick()
assertThat(bubbleData.isExpanded).isTrue()
bubbleStackView.setSelectedBubble(bubble2)
bubbleStackView.isExpanded = true
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(lastUpdate!!.expanded).isTrue()
assertThat(lastUpdate!!.bubbles.map { it.key })
.containsExactly("bubble2", "bubble1")
.inOrder()
// wait for idle to allow the animation to start
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
// wait for the expansion animation to complete before interacting with the bubbles
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
AnimatableScaleMatrix.SCALE_X, AnimatableScaleMatrix.SCALE_Y)
// tap on bubble1 to select it
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubble1.iconView!!.performClick()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
// tap on bubble1 again to collapse the stack
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// we have to set the selected bubble in the stack view manually because we don't have a
// listener wired up.
bubbleStackView.setSelectedBubble(bubble1)
bubble1.iconView!!.performClick()
}
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
assertThat(bubbleData.isExpanded).isFalse()
assertThat(lastUpdate!!.orderChanged).isTrue()
assertThat(lastUpdate!!.bubbles.map { it.key })
.containsExactly("bubble1", "bubble2")
.inOrder()
}
@EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun testCreateStackView_noOverflowContents_noOverflow() {
bubbleStackView =
BubbleStackView(
context,
bubbleStackViewManager,
positioner,
bubbleData,
null,
FloatingContentCoordinator(),
{ sysuiProxy },
shellExecutor
)
assertThat(bubbleData.overflowBubbles).isEmpty()
val bubbleOverflow = bubbleData.overflow
// Overflow shouldn't be attached
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isEqualTo(-1)
}
@EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun testCreateStackView_hasOverflowContents_hasOverflow() {
// Add a bubble to the overflow
val bubble1 = createAndInflateChatBubble(key = "bubble1")
bubbleData.notificationEntryUpdated(bubble1, false, false)
bubbleData.dismissBubbleWithKey(bubble1.key, Bubbles.DISMISS_USER_GESTURE)
assertThat(bubbleData.overflowBubbles).isNotEmpty()
bubbleStackView =
BubbleStackView(
context,
bubbleStackViewManager,
positioner,
bubbleData,
null,
FloatingContentCoordinator(),
{ sysuiProxy },
shellExecutor
)
val bubbleOverflow = bubbleData.overflow
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
}
@DisableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun testCreateStackView_noOverflowContents_hasOverflow() {
bubbleStackView =
BubbleStackView(
context,
bubbleStackViewManager,
positioner,
bubbleData,
null,
FloatingContentCoordinator(),
{ sysuiProxy },
shellExecutor
)
assertThat(bubbleData.overflowBubbles).isEmpty()
val bubbleOverflow = bubbleData.overflow
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
}
@EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun showOverflow_true() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.showOverflow(true)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
val bubbleOverflow = bubbleData.overflow
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
}
@EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun showOverflow_false() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.showOverflow(true)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
val bubbleOverflow = bubbleData.overflow
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.showOverflow(false)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
// The overflow should've been removed
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isEqualTo(-1)
}
@DisableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
@Test
fun showOverflow_ignored() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleStackView.showOverflow(false)
}
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
// showOverflow should've been ignored, so the overflow would be attached
val bubbleOverflow = bubbleData.overflow
assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
}
private fun createAndInflateChatBubble(key: String): Bubble {
val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build()
val bubble =
Bubble(
key,
shortcutInfo,
/* desiredHeight= */ 6,
Resources.ID_NULL,
"title",
/* taskId= */ 0,
"locus",
/* isDismissable= */ true,
directExecutor()
) {}
inflateBubble(bubble)
return bubble
}
private fun createAndInflateBubble(): Bubble {
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor())
inflateBubble(bubble)
return bubble
}
private fun inflateBubble(bubble: Bubble) {
bubble.setInflateSynchronously(true)
bubbleData.notificationEntryUpdated(bubble, true, false)
val semaphore = Semaphore(0)
val callback: BubbleViewInfoTask.Callback =
BubbleViewInfoTask.Callback { semaphore.release() }
bubble.inflate(
callback,
context,
expandedViewManager,
bubbleTaskViewFactory,
positioner,
bubbleStackView,
null,
iconFactory,
false
)
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubble.isInflated).isTrue()
}
private class FakeBubbleStackViewManager : BubbleStackViewManager {
override fun onAllBubblesAnimatedOut() {}
override fun updateWindowFlagsForBackpress(interceptBack: Boolean) {}
override fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>) {}
override fun hideCurrentInputMethod() {}
}
private class TestShellExecutor : ShellExecutor {
override fun execute(runnable: Runnable) {
runnable.run()
}
override fun executeDelayed(r: Runnable, delayMillis: Long) {
r.run()
}
override fun removeCallbacks(r: Runnable?) {}
override fun hasCallback(r: Runnable): Boolean = false
}
private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
override fun create(): BubbleTaskView {
val taskViewTaskController = mock<TaskViewTaskController>()
val taskView = TaskView(context, taskViewTaskController)
return BubbleTaskView(taskView, shellExecutor)
}
}
private inner class FakeBubbleExpandedViewManager : BubbleExpandedViewManager {
override val overflowBubbles: List<Bubble>
get() = emptyList()
override fun setOverflowListener(listener: BubbleData.Listener) {}
override fun collapseStack() {}
override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
override fun promoteBubbleFromOverflow(bubble: Bubble) {}
override fun removeBubble(key: String, reason: Int) {}
override fun dismissBubble(bubble: Bubble, reason: Int) {}
override fun setAppBubbleTaskId(key: String, taskId: Int) {}
override fun isStackExpanded(): Boolean = false
override fun isShowingAsBubbleBar(): Boolean = false
override fun hideCurrentInputMethod() {}
}
}
@@ -1,92 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.bubbles
import android.content.ComponentName
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.wm.shell.taskview.TaskView
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleTaskViewTest {
private lateinit var bubbleTaskView: BubbleTaskView
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var taskView: TaskView
@Before
fun setUp() {
taskView = mock()
bubbleTaskView = BubbleTaskView(taskView, directExecutor())
}
@Test
fun onTaskCreated_updatesState() {
val componentName = ComponentName(context, "TestClass")
bubbleTaskView.listener.onTaskCreated(123, componentName)
assertThat(bubbleTaskView.taskId).isEqualTo(123)
assertThat(bubbleTaskView.componentName).isEqualTo(componentName)
assertThat(bubbleTaskView.isCreated).isTrue()
}
@Test
fun onTaskCreated_callsDelegateListener() {
var actualTaskId = -1
var actualComponentName: ComponentName? = null
val delegateListener = object : TaskView.Listener {
override fun onTaskCreated(taskId: Int, name: ComponentName) {
actualTaskId = taskId
actualComponentName = name
}
}
bubbleTaskView.delegateListener = delegateListener
val componentName = ComponentName(context, "TestClass")
bubbleTaskView.listener.onTaskCreated(123, componentName)
assertThat(actualTaskId).isEqualTo(123)
assertThat(actualComponentName).isEqualTo(componentName)
}
@Test
fun cleanup_invalidTaskId_doesNotRemoveTask() {
bubbleTaskView.cleanup()
verify(taskView, never()).removeTask()
}
@Test
fun cleanup_validTaskId_removesTask() {
val componentName = ComponentName(context, "TestClass")
bubbleTaskView.listener.onTaskCreated(123, componentName)
bubbleTaskView.cleanup()
verify(taskView).removeTask()
}
}
@@ -1,459 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.bubbles.bar
import android.content.Context
import android.graphics.Insets
import android.graphics.PointF
import android.graphics.Rect
import android.view.View
import android.view.WindowManager
import android.widget.FrameLayout
import androidx.core.animation.AnimatorTestRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.bubbles.DeviceConfig
import com.android.wm.shell.common.bubbles.BaseBubblePinController
import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.ClassRule
import org.junit.Test
import org.junit.runner.RunWith
/** Tests for [BubbleExpandedViewPinController] */
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleExpandedViewPinControllerTest {
companion object {
@JvmField @ClassRule val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
const val SCREEN_WIDTH = 2000
const val SCREEN_HEIGHT = 1000
const val BUBBLE_BAR_HEIGHT = 50
}
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var positioner: BubblePositioner
private lateinit var container: FrameLayout
private lateinit var controller: BubbleExpandedViewPinController
private lateinit var testListener: TestLocationChangeListener
private val dropTargetView: View?
get() = container.findViewById(R.id.bubble_bar_drop_target)
private val pointOnLeft = PointF(100f, 100f)
private val pointOnRight = PointF(1900f, 500f)
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
container = FrameLayout(context)
val windowManager = context.getSystemService(WindowManager::class.java)
positioner = BubblePositioner(context, windowManager)
positioner.setShowingInBubbleBar(true)
val deviceConfig =
DeviceConfig(
windowBounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
isLargeScreen = true,
isSmallTablet = false,
isLandscape = true,
isRtl = false,
insets = Insets.of(10, 20, 30, 40)
)
positioner.update(deviceConfig)
positioner.bubbleBarTopOnScreen =
SCREEN_HEIGHT - deviceConfig.insets.bottom - BUBBLE_BAR_HEIGHT
controller = BubbleExpandedViewPinController(context, container, positioner)
testListener = TestLocationChangeListener()
controller.setListener(testListener)
}
@After
fun tearDown() {
getInstrumentation().runOnMainSync { controller.onDragEnd() }
waitForAnimateOut()
}
/** Dragging on same side should not show drop target or trigger location changes */
@Test
fun drag_stayOnRightSide() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onDragEnd()
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).containsExactly(RIGHT)
}
/** Dragging on same side should not show drop target or trigger location changes */
@Test
fun drag_stayOnLeftSide() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onDragEnd()
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).containsExactly(LEFT)
}
/** Drag crosses to the other side. Show drop target and trigger a location change. */
@Test
fun drag_rightToLeft() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
assertThat(testListener.locationChanges).containsExactly(LEFT)
assertThat(testListener.locationReleases).isEmpty()
}
/** Drag crosses to the other side. Show drop target and trigger a location change. */
@Test
fun drag_leftToRight() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
assertThat(testListener.locationChanges).containsExactly(RIGHT)
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drop target does not initially show on the side that the drag starts. Check that it shows up
* after the dragging the view to other side and back to the initial side.
*/
@Test
fun drag_rightToLeftToRight() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
getInstrumentation().runOnMainSync { controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y) }
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
getInstrumentation().runOnMainSync {
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateOut()
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
assertThat(testListener.locationChanges).containsExactly(LEFT, RIGHT).inOrder()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drop target does not initially show on the side that the drag starts. Check that it shows up
* after the dragging the view to other side and back to the initial side.
*/
@Test
fun drag_leftToRightToLeft() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
getInstrumentation().runOnMainSync {
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
getInstrumentation().runOnMainSync { controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y) }
waitForAnimateOut()
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
assertThat(testListener.locationChanges).containsExactly(RIGHT, LEFT).inOrder()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag from right to left, but stay in exclusion rect around the dismiss view. Drop target
* should not show and location change should not trigger.
*/
@Test
fun drag_rightToLeft_inExclusionRect() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
// Exclusion rect is around the bottom center area of the screen
controller.onDragUpdate(SCREEN_WIDTH / 2f - 50, SCREEN_HEIGHT - 100f)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag from left to right, but stay in exclusion rect around the dismiss view. Drop target
* should not show and location change should not trigger.
*/
@Test
fun drag_leftToRight_inExclusionRect() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
// Exclusion rect is around the bottom center area of the screen
controller.onDragUpdate(SCREEN_WIDTH / 2f + 50, SCREEN_HEIGHT - 100f)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag to dismiss target and back to the same side should not cause the drop target to show.
*/
@Test
fun drag_rightToDismissToRight() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onStuckToDismissTarget()
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag to dismiss target and back to the same side should not cause the drop target to show.
*/
@Test
fun drag_leftToDismissToLeft() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onStuckToDismissTarget()
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).isEmpty()
}
/** Drag to dismiss target and other side should show drop target on the other side. */
@Test
fun drag_rightToDismissToLeft() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onStuckToDismissTarget()
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
assertThat(testListener.locationChanges).containsExactly(LEFT)
assertThat(testListener.locationReleases).isEmpty()
}
/** Drag to dismiss target and other side should show drop target on the other side. */
@Test
fun drag_leftToDismissToRight() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onStuckToDismissTarget()
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
assertThat(testListener.locationChanges).containsExactly(RIGHT)
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag to dismiss should trigger a location change to the initial location, if the current
* location is different. And hide the drop target.
*/
@Test
fun drag_rightToLeftToDismiss() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
getInstrumentation().runOnMainSync { controller.onStuckToDismissTarget() }
waitForAnimateOut()
assertThat(dropTargetView!!.alpha).isEqualTo(0f)
assertThat(testListener.locationChanges).containsExactly(LEFT, RIGHT).inOrder()
assertThat(testListener.locationReleases).isEmpty()
}
/**
* Drag to dismiss should trigger a location change to the initial location, if the current
* location is different. And hide the drop target.
*/
@Test
fun drag_leftToRightToDismiss() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
getInstrumentation().runOnMainSync { controller.onStuckToDismissTarget() }
waitForAnimateOut()
assertThat(dropTargetView!!.alpha).isEqualTo(0f)
assertThat(testListener.locationChanges).containsExactly(RIGHT, LEFT).inOrder()
assertThat(testListener.locationReleases).isEmpty()
}
/** Finishing drag should remove drop target and send location update. */
@Test
fun drag_rightToLeftRelease() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
getInstrumentation().runOnMainSync { controller.onDragEnd() }
waitForAnimateOut()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).containsExactly(LEFT)
assertThat(testListener.locationReleases).containsExactly(LEFT)
}
/** Finishing drag should remove drop target and send location update. */
@Test
fun drag_leftToRightRelease() {
getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
getInstrumentation().runOnMainSync { controller.onDragEnd() }
waitForAnimateOut()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).containsExactly(RIGHT)
assertThat(testListener.locationReleases).containsExactly(RIGHT)
}
private fun getExpectedDropTargetBoundsOnLeft(): Rect =
Rect().also {
positioner.getBubbleBarExpandedViewBounds(
true /* onLeft */,
false /* isOverflowExpanded */,
it
)
}
private fun getExpectedDropTargetBoundsOnRight(): Rect =
Rect().also {
positioner.getBubbleBarExpandedViewBounds(
false /* onLeft */,
false /* isOverflowExpanded */,
it
)
}
private fun waitForAnimateIn() {
// Advance animator for on-device test
getInstrumentation().runOnMainSync {
animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_IN_DURATION)
}
}
private fun waitForAnimateOut() {
// Advance animator for on-device test
getInstrumentation().runOnMainSync {
animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_OUT_DURATION)
}
}
private fun View.bounds(): Rect {
return Rect(0, 0, layoutParams.width, layoutParams.height).also { rect ->
rect.offsetTo(x.toInt(), y.toInt())
}
}
internal class TestLocationChangeListener : BaseBubblePinController.LocationChangeListener {
val locationChanges = mutableListOf<BubbleBarLocation>()
val locationReleases = mutableListOf<BubbleBarLocation>()
override fun onChange(location: BubbleBarLocation) {
locationChanges.add(location)
}
override fun onRelease(location: BubbleBarLocation) {
locationReleases.add(location)
}
}
}
@@ -15,11 +15,11 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="M23,5v8h-2V5H3v14h10v2v0H3c-1.1,0 -2,-0.9 -2,-2V5c0,-1.1 0.9,-2 2,-2h18C22.1,3 23,3.9 23,5zM10,8v2.59L5.71,6.29L4.29,7.71L8.59,12H6v2h6V8H10zM19,15c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S20.66,15 19,15z"/>
android:pathData="m464,480 l-90,-90q-11,-11 -11.5,-27.5T374,334q11,-11 28,-11t28,11l90,89v-49q0,-17 11.5,-28.5T560,334q17,0 28.5,11.5T600,374v146q0,17 -11.5,28.5T560,560L414,560q-17,0 -28.5,-11.5T374,520q0,-17 11.5,-28.5T414,480h50ZM760,800q-50,0 -85,-35t-35,-85q0,-50 35,-85t85,-35q50,0 85,35t35,85q0,50 -35,85t-85,35ZM160,800q-33,0 -56.5,-23.5T80,720v-480q0,-33 23.5,-56.5T160,160h640q33,0 56.5,23.5T880,240v200q0,17 -11.5,28.5T840,480q-17,0 -28.5,-11.5T800,440v-200L160,240v480h360q17,0 28.5,11.5T560,760q0,17 -11.5,28.5T520,800L160,800Z"
android:fillColor="#FF000000"/>
</vector>
@@ -15,11 +15,11 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="M11.29,14.71L7,10.41V13H5V7h6v2H8.41l4.29,4.29L11.29,14.71zM21,3H3C1.9,3 1,3.9 1,5v14c0,1.1 0.9,2 2,2h10v0v-2H3V5h18v8h2V5C23,3.9 22.1,3 21,3zM19,15c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S20.66,15 19,15z"/>
android:pathData="M160,800q-33,0 -56.5,-23.5T80,720v-480q0,-33 23.5,-56.5T160,160h640q33,0 56.5,23.5T880,240v200q0,17 -11.5,28.5T840,480q-17,0 -28.5,-11.5T800,440v-200L160,240v480h360q17,0 28.5,11.5T560,760q0,17 -11.5,28.5T520,800L160,800ZM496,400 L586,490q11,11 11,27.5T586,546q-12,12 -28.5,12T529,546l-89,-89v49q0,17 -11.5,28.5T400,546q-17,0 -28.5,-11.5T360,506v-146q0,-17 11.5,-28.5T400,320h146q17,0 28.5,11.5T586,360q0,17 -11.5,28.5T546,400h-50ZM760,800q-50,0 -85,-35t-35,-85q0,-50 35,-85t85,-35q50,0 85,35t35,85q0,50 -35,85t-85,35Z"
android:fillColor="#FF000000"/>
</vector>
@@ -60,6 +60,20 @@
android:focusable="false"
tools:text="Gmail"/>
<ImageView
android:id="@+id/expand_menu_error"
android:src="@drawable/expand_menu_error"
android:layout_width="@dimen/desktop_mode_header_expand_menu_error_image_width"
android:layout_height="@dimen/desktop_mode_header_expand_menu_error_image_width"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/desktop_mode_header_expand_menu_error_image_margin"
android:clickable="false"
android:focusable="false"
android:screenReaderFocusable="false"
android:importantForAccessibility="no"
android:contentDescription="@null"
android:scaleType="centerCrop"/>
<ImageButton
android:id="@+id/expand_menu_button"
android:layout_width="16dp"
@@ -81,6 +81,7 @@
style="@style/DesktopModeHandleMenuWindowingButton"/>
<Space
android:id="@+id/split_screen_button_space"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"/>
@@ -164,12 +165,33 @@
android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
style="@style/DesktopModeHandleMenuActionButton"/>
<com.android.wm.shell.windowdecor.HandleMenuActionButton
android:id="@+id/handle_menu_restart_button"
android:contentDescription="@string/handle_menu_restart_text"
android:text="@string/handle_menu_restart_text"
android:src="@drawable/desktop_mode_ic_handle_menu_restart"
style="@style/DesktopModeHandleMenuActionButton"/>
<LinearLayout
android:id="@+id/handle_menu_restart_pill"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.android.wm.shell.windowdecor.HandleMenuActionButton
android:id="@+id/handle_menu_restart_button"
android:contentDescription="@string/handle_menu_restart_text"
android:text="@string/handle_menu_restart_text"
android:src="@drawable/desktop_mode_ic_handle_menu_restart"
style="@style/DesktopModeHandleMenuActionButton"
android:layout_width="0dp"
android:layout_weight="1"/>
<ImageView
android:id="@+id/restart_menu_error_icon"
android:contentDescription="@string/handle_menu_error_icon_text"
android:layout_gravity="end|center_vertical"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="16dp"
android:src="@drawable/expand_menu_error"
android:clickable="false"
android:focusable="false"
android:scaleType="centerCrop"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
@@ -14,7 +14,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.android.wm.shell.windowdecor.CaptionMenuLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/maximize_menu"
android:layout_width="wrap_content"
@@ -158,12 +159,4 @@
android:alpha="0"/>
</LinearLayout>
</LinearLayout>
<!-- Empty view intentionally placed in front of everything else and matching the menu size
used to monitor input events over the entire menu. -->
<View
android:id="@+id/maximize_menu_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
</com.android.wm.shell.windowdecor.CaptionMenuLayout>
@@ -50,6 +50,7 @@
android:orientation="vertical">
<ImageView
android:id="@+id/letterbox_restart_dialog_title_icon"
android:importantForAccessibility="no"
android:layout_width="@dimen/letterbox_restart_dialog_title_icon_width"
android:layout_height="@dimen/letterbox_restart_dialog_title_icon_height"
+8
View File
@@ -24,6 +24,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.android.wm.shell.common.split.DividerTooltip
android:id="@+id/docked_divider_tooltip"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:visibility="gone" />
<com.android.wm.shell.common.split.DividerHandleView
android:id="@+id/docked_divider_handle"
android:layout_height="match_parent"
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Hierdie app kan net in 1 venster oopgemaak word"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App sal dalk nie op \'n sekondêre skerm werk nie."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App steun nie begin op sekondêre skerms nie."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"App steun nie borrel nie"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Skermverdeler"</string>
<string name="divider_title" msgid="1963391955593749442">"Skermverdeler"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Volskerm links"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Bestuur vensters"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Verander aspekverhouding"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimeer aansig"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Fout-ikoon"</string>
<string name="close_text" msgid="4986518933445178928">"Maak toe"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (werkskermvensters)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimeer skerm"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Verander grootte"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App kan nie hierheen geskuif word nie"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Maak tans nuwe appvenster oop"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Maak tans appvenster toe"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimeer tans appvenster"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (gefokus)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (nie gefokus nie)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Meesleurend"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Stel terug"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeer"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ይህ መተግበሪያ መከፈት የሚችለው በ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="bubble_not_supported_text" msgid="8843806122229754809">"መተግበሪያ ዓረፋን አይደግፍም"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"መስኮቶችን አስተዳድር"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ምጥጥነ ገፅታ ለውጥ"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ዕይታን አትባ"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"የስህተት አዶ"</string>
<string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ዴስክቶፕ መስኮት)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"የማያ ገጹ መጠን አሳድግ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"መጠን ቀይር"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"መተግበሪያ ወደዚህ መንቀሳቀስ አይችልም"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"አዲስ የመተግበሪያ መስኮት በመክፈት ላይ"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"የመተግበሪያ መስኮትን በመዝጋት ላይ"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"የመተግበሪያ መስኮትን በማሳነስ ላይ"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ትኩረት የተደረገበት)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ትኩረት ያልተደረገበት)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"አስማጭ"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ወደነበረበት መልስ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"አሳድግ"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"لا يمكن فتح هذا التطبيق إلا في نافذة واحدة."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"قد لا يعمل التطبيق على شاشة عرض ثانوية."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"لا يمكن تشغيل التطبيق على شاشات عرض ثانوية."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"لا يتيح التطبيق إظهار المهام على شكل فقاعة"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"إدارة النوافذ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغيير نسبة العرض إلى الارتفاع"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"تحسين طريقة العرض"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"رمز الخطأ"</string>
<string name="close_text" msgid="4986518933445178928">"إغلاق"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (عرض المحتوى في النافذة الحالية على سطح المكتب)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"تغيير الحجم"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"لا يمكن نقل التطبيق إلى هنا"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"جارٍ فتح نافذة تطبيق جديدة"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"جارٍ إغلاق نافذة التطبيق"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"جارٍ تصغير نافذة التطبيق"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (تم التركيز عليه)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (غير مركّز عليه)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"مجسَّم"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"استعادة"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"تكبير"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"এই এপ্‌টো কেৱল ১ খন ৱিণ্ড’ত খুলিব পাৰি"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"গৌণ ডিছপ্লেত এপ্ লঞ্চ কৰিব নোৱাৰি।"</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"এপে বাবল সমৰ্থন নকৰে"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ৱিণ্ড’ পৰিচালনা কৰক"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"আকাৰৰ অনুপাত সলনি কৰক"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ভিউ অপ্টিমাইজ কৰক"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"আসোঁৱাহৰ চিহ্ন"</string>
<string name="close_text" msgid="4986518933445178928">"বন্ধ কৰক"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"মেনু বন্ধ কৰক"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ডেস্কটপ ৱিণ্ড’ৱিং)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্ৰীন মেক্সিমাইজ কৰক"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"আকাৰ সলনি কৰক"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ইয়ালৈ এপ্‌টো আনিব নোৱাৰি"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"নতুন এপৰ ৱিণ্ড’ খুলি থকা হৈছে"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"এপৰ ৱিণ্ড’ বন্ধ কৰি থকা হৈছে"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"এপৰ ৱিণ্ড’ মিনিমাইজ কৰা হৈছে"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ফ’কাছ কৰা)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ফ’কাছ কৰা নাই)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ইমাৰ্ছিভ"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"পুনঃস্থাপন কৰক"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"মেক্সিমাইজ কৰক"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Bu tətbiq yalnız 1 pəncərədə açıla bilər"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Tətbiq ikinci ekranda işləməyə bilər."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Tətbiq ikinci ekranda başlamağı dəstəkləmir."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Tətbiq qabarcığı dəstəkləmir"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Bölünmüş ekran ayırıcısı"</string>
<string name="divider_title" msgid="1963391955593749442">"Bölünmüş ekran ayırıcısı"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Sol tam ekran"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Pəncərələri idarə edin"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tərəflər nisbətini dəyişin"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Görünüşü optimallaşdırın"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Xəta ikonası"</string>
<string name="close_text" msgid="4986518933445178928">"Bağlayın"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Masaüstü pəncərə rejimi)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı maksimum böyüdün"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ölçüsünü dəyişin"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Tətbiqi bura köçürmək mümkün deyil"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Yeni tətbiq pəncərəsi açılır"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Tətbiq pəncərəsi bağlanır"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Tətbiq pəncərəsi kiçildilir"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Fokuslanmış)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Fokuslanmamış)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"İmmersiv"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Bərpa edin"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Böyüdün"</string>
+9 -2
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ova aplikacija može da se otvori samo u jednom prozoru"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Aplikacija ne podržava oblačić"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Razdelnik podeljenog ekrana"</string>
<string name="divider_title" msgid="1963391955593749442">"Razdelnik podeljenog ekrana"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Režim celog ekrana za levi ekran"</string>
@@ -77,14 +78,14 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
<string name="bubble_fullscreen_text" msgid="1006758103218086231">"Prebaci na ceo ekran"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne koristi oblačiće za konverzaciju"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Ćaskajte u oblačićima"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Četujte u oblačićima"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Nove konverzacije se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da biste otvorili oblačić. Prevucite da biste ga premestili."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrolišite oblačiće u bilo kom trenutku"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Dodirnite Upravljajte da biste isključili oblačiće iz ove aplikacije"</string>
<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_title" msgid="2486903590422497245">"Četujte 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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljajte prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promeni razmeru"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizujte prikaz"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona greške"</string>
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz računarskih prozora)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Prilagodi"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija ne može da se premesti ovde"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Otvara se novi prozor aplikacije"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Zatvara se prozor aplikacije"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Umanjuje se prozor aplikacije"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fokusirano)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (nije fokusirano)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Imerzivno"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Vrati"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Uvećaj"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Гэту праграму можна адкрыць толькі ў адным акне"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Праграма можа не працаваць на дадатковых экранах."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Праграма не падтрымлівае запуск на дадатковых экранах."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"У праграме не падтрымліваецца ўсплывальны чат"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Кіраваць вокнамі"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змяніць суадносіны бакоў"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Аптымізаваць прагляд"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Значок памылкі"</string>
<string name="close_text" msgid="4986518933445178928">"Закрыць"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрыць меню"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (рэжым вокнаў працоўнага стала)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Разгарнуць на ўвесь экран"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Змяніць памер"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Нельга перамясціць сюды праграму"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Адкрываецца новае акно праграмы"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Закрываецца акно праграмы"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Згортваецца акно праграмы"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (у фокусе)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (не ў фокусе)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"З эфектам прысутнасці"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Аднавіць"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Разгарнуць"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Това приложение може да се отвори само в 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="bubble_not_supported_text" msgid="8843806122229754809">"Приложението не поддържа балончета"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Управление на прозорците"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промяна на съотношението"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Оптимизиране на изгледа"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Икона за грешка"</string>
<string name="close_text" msgid="4986518933445178928">"Затваряне"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим за настолни компютри)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Увеличаване на екрана"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Нов размер"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложението не може да бъде преместено тук"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Отваря се нов прозорец на приложението"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Прозорецът на приложението се затваря"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Прозорецът на приложението се намалява"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (притежава фокуса)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (не притежава фокуса)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Реалистично"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Възстановяване"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увеличаване"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"এই অ্যাপটি শুধুমাত্র ১টি উইন্ডোতে খোলা যেতে পারে"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"সেকেন্ডারি ডিসপ্লেতে অ্যাপটি কাজ নাও করতে পারে।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"সেকেন্ডারি ডিসপ্লেতে অ্যাপ লঞ্চ করা যাবে না।"</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"অ্যাপে বাবল কাজ করে না"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"উইন্ডো ম্যানেজ করুন"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ভিউ অপ্টিমাইজ করুন"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"সমস্যার আইকন"</string>
<string name="close_text" msgid="4986518933445178928">"বন্ধ করুন"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"\'মেনু\' বন্ধ করুন"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ডেস্কটপ উইন্ডোইং)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্রিন বড় করুন"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ছোট বড় করুন"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"অ্যাপটি এখানে সরানো যাবে না"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"নতুন অ্যাপ উইন্ডো খোলা হচ্ছে"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"অ্যাপের উইন্ডো বন্ধ করা হচ্ছে"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"অ্যাপ উইন্ডো ছোট করা হয়েছে"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ফোকাস করা হয়েছে)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ফোকাস করা নেই)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ইমারসিভ"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ফিরিয়ে আনুন"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"বড় করুন"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ova aplikacija se može otvoriti samo u 1 prozoru"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće raditi na sekundarnom ekranu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Aplikacija ne podržava oblačić"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Razdjelnik podijeljenog ekrana"</string>
<string name="divider_title" msgid="1963391955593749442">"Razdjelnik podijeljenog ekrana"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lijevo cijeli ekran"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promjena formata slike"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimiziranje prikaza"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona greške"</string>
<string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (računarski prikaz prozora)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Promijeni veličinu"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ne možete premjestiti aplikaciju ovdje"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Otvaranje novog prozora aplikacije"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Zatvaranje prozora aplikacije"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimiziranje prozora aplikacije"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fokusirano)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (nije fokusirano)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Uvjerljivo"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Vraćanje"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziraj"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Aquesta aplicació només pot obrir-se en 1 finestra"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"És possible que l\'aplicació no funcioni en una pantalla secundària."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'aplicació no es pot obrir en pantalles secundàries."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"L\'aplicació no admet la bombolla"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Separador de pantalla dividida"</string>
<string name="divider_title" msgid="1963391955593749442">"Separador de pantalla dividida"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla esquerra completa"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gestiona les finestres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Canvia la relació d\'aspecte"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Visualització d\'Optimize"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icona d\'error"</string>
<string name="close_text" msgid="4986518933445178928">"Tanca"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tanca el menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (sistema de finestres de tipus escriptori)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Canvia la mida"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"L\'aplicació no es pot moure aquí"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"S\'està obrint una finestra nova de l\'aplicació"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"S\'està tancant la finestra de l\'aplicació"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"S\'està minimitzant la finestra de l\'aplicació"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (té el focus)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (no té el focus)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersiu"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaura"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximitza"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Tuto aplikaci lze otevřít jen v jednom okně"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikace na sekundárním displeji nemusí fungovat."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikace nepodporuje spuštění na sekundárních displejích."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Aplikace bublinu nepodporuje"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Čára rozdělující obrazovku"</string>
<string name="divider_title" msgid="1963391955593749442">"Čára rozdělující obrazovku"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Levá část na celou obrazovku"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Spravovat okna"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Změnit poměr stran"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimalizovat zobrazení"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona chyby"</string>
<string name="close_text" msgid="4986518933445178928">"Zavřít"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (okna na ploše)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Změnit velikost"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikaci sem nelze přesunout"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Otevírání nového okna aplikace"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Zavírání okna aplikace"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimalizace okna aplikace"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (označeno)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (neoznačeno)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Pohlcující"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Obnovit"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovat"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Denne app kan kun åbnes i 1 vindue"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer muligvis ikke på sekundære skærme."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke åbnes på sekundære skærme."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Appen understøtter ikke bobler"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Adskiller til opdelt skærm"</string>
<string name="divider_title" msgid="1963391955593749442">"Adskiller til opdelt skærm"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vis venstre del i fuld skærm"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduer"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Skift billedformat"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimer visning"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Fejlikon"</string>
<string name="close_text" msgid="4986518933445178928">"Luk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (vinduer på skrivebordet)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimér skærm"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Tilpas størrelse"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apps kan ikke flyttes hertil"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Et nyt appvindue er åbnet"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Lukker appvindue"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Appvinduet er minimeret"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fremhævet)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ikke fremhævet)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Opslugende"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Gendan"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimér"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"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>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"App unterstützt keine Bubbles"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Bildschirmteiler"</string>
<string name="divider_title" msgid="1963391955593749442">"Bildschirmteiler"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vollbild links"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Fenster verwalten"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Seitenverhältnis ändern"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Ansicht optimieren"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Fehlersymbol"</string>
<string name="close_text" msgid="4986518933445178928">"Schließen"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop-Freiform-Fenster)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Bildschirm maximieren"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Größe ändern"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Die App kann nicht hierher verschoben werden"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Neues App-Fenster wird geöffnet"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"App-Fenster wird geschlossen"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"App-Fenster wird minimiert"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Fokussiert)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Nicht fokussiert)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersiv"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Wiederherstellen"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximieren"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Αυτή η εφαρμογή μπορεί να ανοίξει μόνο σε ένα παράθυρο"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Η εφαρμογή ίσως να μην λειτουργήσει σε δευτερεύουσα οθόνη."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Η εφαρμογή δεν υποστηρίζει την εκκίνηση σε δευτερεύουσες οθόνες."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Η εφαρμογή δεν υποστηρίζει συννεφάκια"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Διαχείριση παραθύρων"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Αλλαγή λόγου διαστάσεων"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Βελτιστοποίηση προβολής"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Εικονίδιο σφάλματος"</string>
<string name="close_text" msgid="4986518933445178928">"Κλείσιμο"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Προσαρμογή σε παράθυρο στην επιφάνεια εργασίας)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Αλλαγή μεγέθους"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Δεν είναι δυνατή η μετακίνηση της εφαρμογής εδώ"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Άνοιγμα νέου παραθύρου εφαρμογής"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Κλείσιμο παραθύρου εφαρμογής"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Ελαχιστοποίηση παραθύρου εφαρμογής"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Με εστίαση)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Χωρίς εστίαση)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Καθηλωτικό"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Επαναφορά"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Μεγιστοποίηση"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"This app can only be opened in one window"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"App does not support bubble"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Split screen divider"</string>
<string name="divider_title" msgid="1963391955593749442">"Split screen divider"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimise view"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Error icon"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Opening new app window"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Closing app window"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimising app window"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (focused)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (not focused)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"This app can only be opened in 1 window"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"App does not support bubble"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Split screen divider"</string>
<string name="divider_title" msgid="1963391955593749442">"Split screen divider"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Manage Windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimize View"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Error Icon"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximize Screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Opening new app window"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Closing app window"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimizing app window"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Focused)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Not Focused)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximize"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"This app can only be opened in one window"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"App does not support bubble"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Split screen divider"</string>
<string name="divider_title" msgid="1963391955593749442">"Split screen divider"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimise view"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Error icon"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Opening new app window"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Closing app window"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimising app window"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (focused)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (not focused)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"This app can only be opened in one window"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"App does not support bubble"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Split screen divider"</string>
<string name="divider_title" msgid="1963391955593749442">"Split screen divider"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimise view"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Error icon"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Opening new app window"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Closing app window"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimising app window"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (focused)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (not focused)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersive"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restore"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Esta app solo puede estar abierta en 1 ventana"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la app no funcione en una pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La app no puede iniciarse en pantallas secundarias."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"La app no admite la burbuja"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Divisor de pantalla dividida"</string>
<string name="divider_title" msgid="1963391955593749442">"Divisor de pantalla dividida"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla izquierda completa"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Administrar ventanas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizar vista"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ícono de error"</string>
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (renderización en ventanas de escritorio)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Dividir pantalla"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"No se puede mover la app aquí"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Abriendo una nueva ventana de la app"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Cerrando la ventana de la app"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimizando la ventana de la app"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (enfocada)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (sin enfocar)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Inmersivo"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restablecer"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Esta aplicación solo puede abrirse en una ventana"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la aplicación no funcione en una pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La aplicación no se puede abrir en pantallas secundarias."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"La aplicación no es compatible con burbujas"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Divisor de pantalla dividida"</string>
<string name="divider_title" msgid="1963391955593749442">"Divisor de pantalla dividida"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla izquierda completa"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gestionar ventanas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizar vista"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icono de error"</string>
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (modo Ventanas)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Dividir pantalla"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"La aplicación no se puede mover aquí"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Abriendo nueva ventana de la aplicación"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Cerrando ventana de la aplicación"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimizando ventana de la aplicación"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (enfocada)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (no enfocada)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Inmersivo"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Selle rakenduse saab avada ainult ühes aknas"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Rakendus ei pruugi teisesel ekraanil töötada."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Rakendus ei toeta teisestel ekraanidel käivitamist."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Rakendus ei toeta mulle"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Jagatud ekraanikuva jaotur"</string>
<string name="divider_title" msgid="1963391955593749442">"Jagatud ekraanikuva jaotur"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vasak täisekraan"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Akende haldamine"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Kuvasuhte muutmine"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Vaate optimeerimine"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Veaikoon"</string>
<string name="close_text" msgid="4986518933445178928">"Sule"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (töölaua aknad)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Suuruse muutmine"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Rakendust ei saa siia teisaldada"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Rakenduse avamine uues aknas"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Rakenduse akna sulgemine"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Rakenduse akna minimeerimine"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fookuses)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (mitte fookuses)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Mahuta"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Taasta"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeeri"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"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 exekutatu bigarren mailako pantailatan."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Aplikazioak ez ditu onartzen burbuilak"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Kudeatu leihoak"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Aldatu aspektu-erlazioa"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizatu ikuspegia"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Errore-ikonoa"</string>
<string name="close_text" msgid="4986518933445178928">"Itxi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Itxi menua"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ordenagailuan leihoak erabiltzeko modua)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Handitu pantaila"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Aldatu tamaina"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikazioa ezin da hona ekarri"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Ireki da aplikazioaren leiho berri bat"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Aplikazioaren leihoa ixten"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimizatu da aplikazioaren leihoa"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fokuratuta)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fokuratu gabe)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Murgiltzailea"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Leheneratu"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizatu"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"این برنامه فقط در ۱ پنجره می‌تواند باز شود"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن است برنامه در نمایشگر ثانویه کار نکند."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"برنامه از راه‌اندازی در نمایشگرهای ثانویه پشتیبانی نمی‌کند."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"این برنامه از حبابک پشتیبانی نمی‌کند"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"مدیریت کردن پنجره‌ها"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغییر نسبت ابعادی"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"بهینه‌سازی نما"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"نماد خطا"</string>
<string name="close_text" msgid="4986518933445178928">"بستن"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (پردازش پنجره‌ای رایانه)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"تغییر اندازه"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"برنامه را نمی‌توان به اینجا منتقل کرد"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"باز کردن پنجره برنامه جدید"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"بستن پنجره برنامه"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"کوچک کردن پنجره برنامه"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (کانونی‌شده)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (کانونی‌نشده)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"فراگیر"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"بازیابی"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بزرگ کردن"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Tämän sovelluksen voi avata vain yhdessä ikkunassa"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Sovellus ei ehkä toimi toissijaisella näytöllä."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Sovellus ei tue käynnistämistä toissijaisilla näytöillä."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Sovellus ei tue kuplia"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Näytönjakaja"</string>
<string name="divider_title" msgid="1963391955593749442">"Näytönjakaja"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vasen koko näytölle"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Hallinnoi ikkunoita"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Vaihda kuvasuhdetta"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimoi näkymä"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Virhekuvake"</string>
<string name="close_text" msgid="4986518933445178928">"Sulje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (työpöydän ikkunointi)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Suurenna näyttö"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Muuta kokoa"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Sovellusta ei voi siirtää tänne"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Avataan uutta sovellusikkunaa"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Suljetaan sovellusikkunaa"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Pienennetään sovellusikkunaa"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Keskitetty)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Ei keskitetty)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersiivinen"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Palauta"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Suurenna"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Cette appli ne peut être ouverte que dans une seule fenêtre."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'appli ne fonctionne pas sur un écran secondaire."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'appli ne peut pas être lancée sur des écrans secondaires."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"L\'appli ne prend pas en charge la bulle"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Séparateur d\'écran partagé"</string>
<string name="divider_title" msgid="1963391955593749442">"Séparateur d\'écran partagé"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Plein écran à la gauche"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier les proportions"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimiser l\'affichage"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icône d\'erreur"</string>
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fenêtrage du bureau)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Agrandir l\'écran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionner"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Ouverture d\'une nouvelle fenêtre d\'appli en cours…"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Fermeture de la fenêtre de l\'appli en cours…"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Réduction de la fenêtre d\'appli en cours…"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (mise en évidence)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (non mise en évidence)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersif"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurer"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string>
+8 -1
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Cette appli ne peut être ouverte que dans 1 fenêtre"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"L\'appli n\'est pas compatible avec les bulles"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Séparateur d\'écran partagé"</string>
<string name="divider_title" msgid="1963391955593749442">"Séparateur d\'écran partagé"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Écran de gauche en plein écran"</string>
@@ -55,7 +56,7 @@
<string name="accessibility_split_top" msgid="2789329702027147146">"Affichée en haut"</string>
<string name="accessibility_split_bottom" msgid="8694551025220868191">"Affichée en haut"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utilisation du mode une main"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pour quitter, balayez l\'écran de bas en haut ou appuyez n\'importe où au-dessus de l\'application"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pour quitter, balayez vers le haut depuis le bas de l\'écran ou appuyez n\'importe où au-dessus de l\'application"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Démarrer le mode une main"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Quitter le mode une main"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Paramètres des bulles de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier le format"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimiser l\'affichage"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icône Erreur"</string>
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fenêtrage de bureau)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mettre en plein écran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionner"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Ouverture d\'une nouvelle fenêtre de l\'application"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Fermeture de la fenêtre de l\'application"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Réduction de la fenêtre de l\'application"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (sélectionné)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (non sélectionné)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersif"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurer"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Esta aplicación só se pode abrir en 1 ventá"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É posible que a aplicación non funcione nunha pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"A aplicación non se pode iniciar en pantallas secundarias."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"A aplicación non admite as burbullas"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Divisor de pantalla dividida"</string>
<string name="divider_title" msgid="1963391955593749442">"Divisor de pantalla dividida"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla completa á esquerda"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Xestionar as ventás"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar a proporción"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizar a vista"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icona de erro"</string>
<string name="close_text" msgid="4986518933445178928">"Pechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Pechar o menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (modo con ventás tipo ordenador)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Cambiar tamaño"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Non se pode mover aquí a aplicación"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Abrindo nova ventá da aplicación"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Pechando ventá da aplicación"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimizando ventá da aplicación"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (enfocada)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (non enfocada)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Envolvente"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"આ ઍપ માત્ર 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="bubble_not_supported_text" msgid="8843806122229754809">"ઍપ બબલને સપોર્ટ કરતી નથી"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"વિન્ડો મેનેજ કરો"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"સાપેક્ષ ગુણોત્તર બદલો"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"વ્યૂ ઑપ્ટિમાઇઝ કરો"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"ભૂલનું આઇકન"</string>
<string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ડેસ્કટૉપ વિન્ડોઇંગ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"સ્ક્રીન કરો મોટી કરો"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"કદ બદલો"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ઍપ અહીં ખસેડી શકાતી નથી"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"નવી વિન્ડો ખોલવી"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"ઍપની વિન્ડો બંધ કરી રહ્યાં છીએ"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"ઍપ વિન્ડો ન્યૂનતમ કરવી"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ફોકસ કરેલ)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ફોકસ ન કરેલી)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ઇમર્સિવ"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"રિસ્ટોર કરો"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"મોટું કરો"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"इस ऐप्लिकेशन को सिर्फ़ एक विंडो में खोला जा सकता है"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"हो सकता है कि ऐप प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर काम न करे."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर ऐप लॉन्च नहीं किया जा सकता."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"इस ऐप्लिकेशन पर बबल की सुविधा काम नहीं करती"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"विंडो मैनेज करें"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"व्यू को ऑप्टिमाइज़ करें"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"गड़बड़ी का आइकॉन"</string>
<string name="close_text" msgid="4986518933445178928">"बंद करें"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप विंडोविंग)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन को बड़ा करें"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"साइज़ बदलें"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ऐप्लिकेशन को यहां मूव नहीं किया जा सकता"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"ऐप्लिकेशन की नई विंडो खोली जा रही है"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"ऐप्लिकेशन की विंडो बंद की जा रही है"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"ऐप्लिकेशन की विंडो को छोटा किया जा रहा है"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (फ़ोकस किया गया)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (फ़ोकस नहीं किया गया)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"इमर्सिव"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"वापस लाएं"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"बड़ा करें"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ova se aplikacija može otvoriti samo u jednom prozoru"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionirati na sekundarnom zaslonu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim zaslonima."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Aplikacija ne podržava oblačić"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Razdjelnik podijeljenog zaslona"</string>
<string name="divider_title" msgid="1963391955593749442">"Razdjelnik podijeljenog zaslona"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lijevi zaslon u cijeli zaslon"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promijeni omjer slike"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimiziraj prikaz"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikona pogreške"</string>
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz u prozorima na računalu)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimalno povećaj zaslon"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Promijeni veličinu"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija se ne može premjestiti ovdje"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Otvaranje novog prozora aplikacije"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Zatvaranje prozora aplikacije"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimiziranje prozora aplikacije"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fokusirano)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (nije fokusirano)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Interaktivno"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Vrati"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziraj"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Ez az alkalmazás csak egy ablakban nyitható meg"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Előfordulhat, hogy az alkalmazás nem működik másodlagos kijelzőn."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Az alkalmazást nem lehet másodlagos kijelzőn elindítani."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Az alkalmazás nem támogatja a buborékot"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Elválasztó az osztott képernyős nézetben"</string>
<string name="divider_title" msgid="1963391955593749442">"Elválasztó az osztott képernyős nézetben"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Bal oldali teljes képernyőre"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Ablakok kezelése"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Méretarány módosítása"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Nézet optimalizálása"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Hiba ikon"</string>
<string name="close_text" msgid="4986518933445178928">"Bezárás"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Asztali ablakkezelési mód)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Képernyő méretének maximalizálása"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Átméretezés"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Az alkalmazás nem helyezhető át ide"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Új alkalmazásablak megnyitása…"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Alkalmazásablak bezárása"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Alkalmazásablak kis méretre állítása…"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Fókuszált)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Nincs fókuszban)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Magával ragadó"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Visszaállítás"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Teljes méret"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Այս հավելվածը հնարավոր է բացել միայն մեկ պատուհանում"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Հավելվածը կարող է չաշխատել լրացուցիչ էկրանի վրա"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Հավելվածը չի աջակցում գործարկումը լրացուցիչ էկրանների վրա"</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Հավելվածը չի աջակցում ամպիկ"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Կառավարել պատուհանները"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Փոխել կողմերի հարաբերակցությունը"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Օպտիմալացնել"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Սխալի պատկերակ"</string>
<string name="close_text" msgid="4986518933445178928">"Փակել"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (համակարգչային պատուհաններ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Փոխել չափը"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Հավելվածը հնարավոր չէ տեղափոխել այստեղ"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Հավելվածի նոր պատուհանը բացվում է"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Հավելվածի պատուհանը փակվում է"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Հավելվածի պատուհանը ծալվում է"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"Ընտրված է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն ընտրված չէ"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Ներկայության էֆեկտով"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Վերականգնել"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ծավալել"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Aplikasi ini hanya dapat dibuka di 1 jendela"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikasi mungkin tidak berfungsi pada layar sekunder."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikasi tidak mendukung peluncuran pada layar sekunder."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Aplikasi tidak mendukung balon"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Pembagi layar terpisah"</string>
<string name="divider_title" msgid="1963391955593749442">"Pembagi layar terpisah"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Layar penuh di kiri"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Kelola Jendela"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ubah rasio aspek"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimalkan Tampilan"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikon Error"</string>
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Mode jendela desktop)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Perbesar Layar"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ubah ukuran"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikasi tidak dapat dipindahkan ke sini"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Membuka jendela aplikasi baru"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Menutup jendela aplikasi"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Meminimalkan jendela aplikasi"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Difokuskan)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Tidak Difokuskan)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Imersif"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Pulihkan"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimalkan"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Aðeins er hægt að opna þetta forrit í 1 glugga"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Hugsanlegt er að forritið virki ekki á öðrum skjá."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Forrit styður ekki opnun á öðrum skjá."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Forrit styður ekki blöðru"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Skilrúm skjáskiptingar"</string>
<string name="divider_title" msgid="1963391955593749442">"Skilrúm skjáskiptingar"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vinstri á öllum skjánum"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Stjórna gluggum"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Breyta myndhlutfalli"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Fínstilla yfirlit"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Villutákn"</string>
<string name="close_text" msgid="4986518933445178928">"Loka"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (gluggastilling í tölvu)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Stækka skjá"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Breyta stærð"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ekki er hægt að færa forritið hingað"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Opnar nýjan forritsglugga"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Lokar forritsglugga"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minnkar forritsglugga"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (í fókus)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ekki í fókus)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Umlykjandi"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Endurheimta"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Stækka"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Questa app può essere aperta soltanto in 1 finestra"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"L\'app potrebbe non funzionare su un display secondario."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'app non supporta l\'avvio su display secondari."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"L\'app non supporta la riduzione a icona"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Strumento per schermo diviso"</string>
<string name="divider_title" msgid="1963391955593749442">"Strumento per schermo diviso"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Schermata sinistra a schermo intero"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Gestisci finestre"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambia proporzioni"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Ottimizza visualizzazione"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Icona di errore"</string>
<string name="close_text" msgid="4986518933445178928">"Chiudi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (app in finestre)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Massimizza schermo"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ridimensiona"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossibile spostare l\'app qui"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Apertura di una nuova finestra dell\'app"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Chiusura della finestra app in corso…"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Riduzione a icona della finestra dell\'app"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (selezionata)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (non selezionata)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersivo"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Ripristina"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ingrandisci"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ניתן לפתוח את האפליקציה הזו רק בחלון אחד"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ייתכן שהאפליקציה לא תפעל במסך משני."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"האפליקציה אינה תומכת בהפעלה במסכים משניים."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"האפליקציה לא תומכת בבועה"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ניהול החלונות"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"שינוי יחס הגובה-רוחב"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"אופטימיזציה של התצוגה"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"סמל שגיאה"</string>
<string name="close_text" msgid="4986518933445178928">"סגירה"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"סגירת התפריט"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (שינוי דינמי של חלונות במחשב)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"הגדלת המסך"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"שינוי הגודל"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"לא ניתן להעביר את האפליקציה לכאן"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"נפתח חלון חדש באפליקציה"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"החלון של האפליקציה עומד להיסגר"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"חלון האפליקציה התמזער"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (במיקוד)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (לא במיקוד)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"הטמעה"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"שחזור"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"הגדלה"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"このアプリはウィンドウが 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="bubble_not_supported_text" msgid="8843806122229754809">"アプリがふきだしに対応していません"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ウィンドウを管理する"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"アスペクト比を変更"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ビューを最適化"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"エラーアイコン"</string>
<string name="close_text" msgid="4986518933445178928">"閉じる"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(デスクトップ ウィンドウ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"画面の最大化"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"サイズ変更"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"アプリはここに移動できません"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"新しいアプリ ウィンドウを開いています"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"アプリのウィンドウを閉じています"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"アプリ ウィンドウを最小化しています"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g>(フォーカス)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g>(非フォーカス)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"没入モード"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"復元"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ამ აპის გახსნა შესაძლებელია მხოლოდ 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="bubble_not_supported_text" msgid="8843806122229754809">"ბუშტი არ არის მხარდაჭერილი აპის მიერ"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ფანჯრების მართვა"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"თანაფარდობის შეცვლა"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ხედის ოპტიმიზება"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"შეცდომის ხატულა"</string>
<string name="close_text" msgid="4986518933445178928">"დახურვა"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"მენიუს დახურვა"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (დესკტოპის ფანჯრის რეჟიმი)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"აპლიკაციის გაშლა სრულ ეკრანზე"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ზომის შეცვლა"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"აპის აქ გადატანა შეუძლებელია"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"აპის ახალი ფანჯარა იხსნება"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"მიმდინარეობს აპის ფანჯრის დახურვა"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"აპის ფანჯარა იკეცება"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ფოკუსირებული)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ფოკუსის გარეთ)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"იმერსიული"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"აღდგენა"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"მაქსიმალურად გაშლა"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Бұл қолданбаны тек 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="bubble_not_supported_text" msgid="8843806122229754809">"Қолданба қалқыма терезеге қолдау көрсетпейді."</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Терезелерді басқару"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Арақатынасты өзгерту"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Көріністі оңтайландыру"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Қате белгішесі"</string>
<string name="close_text" msgid="4986518933445178928">"Жабу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (компьютерлік терезелер режимі)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Өлшемін өзгерту"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Қолданба бұл жерге қойылмайды."</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Жаңа қолданба терезесін ашу"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Қолданба терезесі жабылып жатыр"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Қолданба терезесін кішірейту"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ерекшеленген)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ерекшеленбеген)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Иммерсивтік әсер"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Қалпына келтіру"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Жаю"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"អាចបើកកម្មវិធីនេះបានតែក្នុងវិនដូ 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="bubble_not_supported_text" msgid="8843806122229754809">"កម្មវិធីមិនអាចប្រើផ្ទាំងសារបានទេ"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"គ្រប់គ្រង​វិនដូ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ប្ដូរ​​សមាមាត្រ"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"បង្កើនប្រសិទ្ធភាពការមើល"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"រូបបញ្ហា"</string>
<string name="close_text" msgid="4986518933445178928">"បិទ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"បិទ​ម៉ឺនុយ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (មុខងារវិនដូកុំព្យូទ័រ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ពង្រីកអេក្រង់"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ប្ដូរ​ទំហំ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"មិនអាចផ្លាស់ទីកម្មវិធីមកទីនេះបានទេ"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"ការបើកវិនដូកម្មវិធីថ្មី"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"កំពុងបិទវិនដូកម្មវិធី"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"ការបង្រួមវិនដូកម្មវិធី"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (បានផ្ដោត)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (មិនបានផ្ដោត)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ជក់ចិត្ត"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ស្ដារ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ពង្រីក"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ಈ ಆ್ಯಪ್ ಅನ್ನು 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="bubble_not_supported_text" msgid="8843806122229754809">"ಬಬಲ್ ಅನ್ನು ಆ್ಯಪ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ವಿಂಡೋಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ವೀಕ್ಷಣೆಯನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಿ"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"ದೋಷದ ಐಕಾನ್"</string>
<string name="close_text" msgid="4986518933445178928">"ಮುಚ್ಚಿ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ಮೆನು ಮುಚ್ಚಿ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ಡೆಸ್ಕ್‌ಟಾಪ್ ವಿಂಡೋಯಿಂಗ್)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ಮರುಗಾತ್ರಗೊಳಿಸಿ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ಆ್ಯಪ್ ಅನ್ನು ಇಲ್ಲಿಗೆ ಸರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"ಹೊಸ ಆ್ಯಪ್ ವಿಂಡೋವನ್ನು ತೆರೆಯಲಾಗುತ್ತಿದೆ"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"ಆ್ಯಪ್ ವಿಂಡೋವನ್ನು ಮುಚ್ಚಲಾಗುತ್ತಿದೆ"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"ಆ್ಯಪ್ ವಿಂಡೋವನ್ನು ಮಿನಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ಫೋಕಸ್ ಮಾಡಲಾಗಿದೆ)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ಫೋಕಸ್ ಮಾಡಲಾಗಿಲ್ಲ)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ಇಮ್ಮರ್ಸಿವ್"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ಮರುಸ್ಥಾಪಿಸಿ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
+1 -1
View File
@@ -21,7 +21,7 @@
<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_move" msgid="158770205886688553">"ಸರಿಸಿ"</string>
<string name="pip_move" msgid="158770205886688553">"ಮೂವ್"</string>
<string name="pip_expand" msgid="1051966011679297308">"ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
<string name="pip_collapse" msgid="3903295106641385962">"ಕುಗ್ಗಿಸಿ"</string>
<string name="pip_edu_text" msgid="7930546669915337998">"ಕಂಟ್ರೋಲ್‌ಗಳಿಗಾಗಿ "<annotation icon="home_icon">"ಹೋಮ್"</annotation>" ಅನ್ನು ಎರಡು ಬಾರಿ ಒತ್ತಿರಿ"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"이 앱은 창 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="bubble_not_supported_text" msgid="8843806122229754809">"앱이 풍선을 지원하지 않습니다."</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"창 관리"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"가로세로 비율 변경"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"뷰 최적화"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"오류 아이콘"</string>
<string name="close_text" msgid="4986518933445178928">"닫기"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(데스크톱 윈도윙)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"화면 최대화"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"크기 조절"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"앱을 여기로 이동할 수 없음"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"새 앱 창 열기"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"앱 창 닫기"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"앱 창 최소화"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g>(포커스)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g>(포커스되지 않음)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"몰입형"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"복원"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"최대화하기"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Бул колдонмону 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="bubble_not_supported_text" msgid="8843806122229754809">"Колдонмо көбүкчөнү колдоого албайт"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Терезелерди тескөө"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Тараптардын катнашын өзгөртүү"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Көрүнүштү оптималдаштыруу"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ката сүрөтчөсү"</string>
<string name="close_text" msgid="4986518933445178928">"Жабуу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Менюну жабуу"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Иш тактанын терезелери)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды чоңойтуу"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Өлчөмүн өзгөртүү"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Колдонмону бул жерге жылдырууга болбойт"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Колдонмонун жаңы терезесин ачуу"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Колдонмо терезеси жабылууда"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Колдонмонун терезесин кичирейтүү"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Иштеп жатат)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Иштебей турат)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Сүңгүтүүчү"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Калыбына келтирүү"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Чоңойтуу"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ແອັບນີ້ສາມາດເປີດໄດ້ໃນ 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="bubble_not_supported_text" msgid="8843806122229754809">"ແອັບບໍ່ຮອງຮັບຟອງ"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ຈັດການໜ້າຈໍ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ປ່ຽນອັດຕາສ່ວນຮູບ"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ມຸມມອງເພີ່ມປະສິດທິພາບ"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"ໄອຄອນຂໍ້ຜິດພາດ"</string>
<string name="close_text" msgid="4986518933445178928">"ປິດ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ປິດເມນູ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ໜ້າຈໍເດັສທັອບ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ປັບຈໍໃຫຍ່ສຸດ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ປັບຂະໜາດ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ບໍ່ສາມາດຍ້າຍແອັບມາບ່ອນນີ້ໄດ້"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"ກຳລັງເປີດໜ້າຈໍແອັບໃໝ່"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"ກຳລັງປິດໜ້າຈໍແອັບ"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"ກຳລັງຫຍໍ້ໜ້າຈໍແອັບ"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ໂຟກັສ)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ບໍ່ໂຟກັສ)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ສົມຈິງ"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ກູ້ຄືນ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Šią programą galima atidaryti tik viename lange"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Programa gali neveikti antriniame ekrane."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Programa nepalaiko paleisties antriniuose ekranuose."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Programa nepalaiko pokalbio debesėlio"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Išskaidyto ekrano režimo daliklis"</string>
<string name="divider_title" msgid="1963391955593749442">"Išskaidyto ekrano režimo daliklis"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Kairysis ekranas viso ekrano režimu"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Tvarkyti langus"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Keisti kraštinių santykį"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizuoti rodinį"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Klaidos piktograma"</string>
<string name="close_text" msgid="4986518933445178928">"Uždaryti"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ (versijos staliniams kompiuteriams rodinys)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Išskleisti ekraną"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Pakeisti dydį"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Programos negalima perkelti čia"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Atidaromas naujas programos langas"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Uždaromas programos langas"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Sumažinamas programos langas"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ (paryškinta)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ (neparyškinta)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Įtraukiantis"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Atkurti"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Padidinti"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Šo lietotni var atvērt tikai vienā logā"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Lietotne, iespējams, nedarbosies sekundārajā displejā."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Lietotnē netiek atbalstīta palaišana sekundārajos displejos."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Lietotnē netiek atbalstīts burbulis"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Ekrāna sadalītājs"</string>
<string name="divider_title" msgid="1963391955593749442">"Ekrāna sadalītājs"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Kreisā daļa pa visu ekrānu"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Pārvaldīt logus"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mainīt malu attiecību"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimizēt skatu"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Kļūdas ikona"</string>
<string name="close_text" msgid="4986518933445178928">"Aizvērt"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (darbvirsmas logošana)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizēt ekrānu"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Mainīt lielumu"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Lietotni nevar pārvietot šeit."</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Tiek atvērts jauns lietotnes logs"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Tiek aizvērts lietotnes logs"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Tiek minimizēts lietotnes logs"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fokusā)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (nav fokusā)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Iekļaujoši"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Atjaunot"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizēt"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Апликацијава може да се отвори само во еден прозорец"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликацијата може да не функционира на друг екран."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Апликацијата не поддржува стартување на други екрани."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Апликацијата не поддржува балонче"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Управувајте со прозорците"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени го соодносот"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Оптимизирај го приказот"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Икона за грешка"</string>
<string name="close_text" msgid="4986518933445178928">"Затворете"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим со прозорци на работната површина)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Максимизирај го екранот"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Смени големина"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликацијата не може да се премести овде"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Отворање нов прозорец на апликацијата"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Се затвора прозорецот на апликацијата"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Минимизирање на прозорецот на апликацијата"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (фокусирано)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (не е фокусирано)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Реалистично"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Врати"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Максимизирај"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ഈ ആപ്പ് ഒരു വിൻഡോയിൽ മാത്രമേ തുറക്കാനാകൂ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"രണ്ടാം ഡിസ്‌പ്ലേയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"രണ്ടാം ഡിസ്‌പ്ലേകളിൽ സമാരംഭിക്കുന്നതിനെ ആപ്പ് അനുവദിക്കുന്നില്ല."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"ബബിളിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"വിൻഡോകൾ മാനേജ് ചെയ്യുക"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"വീക്ഷണ അനുപാതം മാറ്റുക"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"കാഴ്‌ച ഒപ്റ്റിമൈസ് ചെയ്യുക"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"പിശക് ഐക്കൺ"</string>
<string name="close_text" msgid="4986518933445178928">"അടയ്ക്കുക"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ഡെസ്ക്ടോപ്പ് വിൻഡോയിംഗ്)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"സ്‌ക്രീൻ വലുതാക്കുക"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"വലുപ്പം മാറ്റുക"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ആപ്പ് ഇവിടേക്ക് നീക്കാനാകില്ല"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"പുതിയ ആപ്പ് വിൻഡോ തുറക്കുന്നു"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"ആപ്പ് വിൻഡോ അടയ്‌ക്കുന്നു"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"ആപ്പ് വിൻഡോ മിനിമൈസ് ചെയ്യുന്നു"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ഫോക്കസ് ചെയ്‌തവ)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ഫോക്കസ് ചെയ്യാത്തവ)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ഇമേഴ്‌സീവ്"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"പുനഃസ്ഥാപിക്കുക"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"വലുതാക്കുക"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Энэ аппыг зөвхөн 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="bubble_not_supported_text" msgid="8843806122229754809">"Апп бөмбөлгийг дэмждэггүй"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Цонхнуудыг удирдах"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Аспектын харьцааг өөрчлөх"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Харагдах байдлыг оновчлох"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Алдааны дүрс тэмдэг"</string>
<string name="close_text" msgid="4986518933445178928">"Хаах"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Дэлгэцийн цонх үүсгэх онцлог)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Дэлгэцийг томруулах"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Хэмжээг өөрчлөх"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Аппыг ийш зөөх боломжгүй"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Аппын шинэ цонхыг нээж байна"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Аппын цонхыг хааж байна"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Аппын цонхыг жижгэрүүлж байна"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (сонгосон)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (сонгоогүй)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Бодит мэт"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Сэргээх"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Томруулах"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"हे अ‍ॅप फक्त एका विंडोमध्ये उघडले जाऊ शकते"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"दुसऱ्या डिस्प्लेवर अ‍ॅप कदाचित चालणार नाही."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"दुसऱ्या डिस्प्लेवर अ‍ॅप लाँच होणार नाही."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"अ‍ॅप बबलला सपोर्ट करत नाही"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"विंडो व्यवस्थापित करा"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"आस्पेक्ट रेशो बदला"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"दृश्य ऑप्टिमाइझ करा"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"एरर आयकन"</string>
<string name="close_text" msgid="4986518933445178928">"बंद करा"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनू बंद करा"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप विंडोइंग)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन मोठी करा"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"आकार बदला"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"अ‍ॅप इथे हलवू शकत नाही"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"नवीन अ‍ॅप विंडो उघडत आहे"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"अ‍ॅपची विंडो बंद करत आहे"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"अ‍ॅप विंडो लहान करत आहे"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (फोकस केलेले)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (फोकस न केलेले)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"इमर्सिव्ह"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"रिस्टोअर करा"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"मोठे करा"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Apl ini hanya boleh dibuka dalam 1 tetingkap"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Apl mungkin tidak berfungsi pada paparan kedua."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Apl tidak menyokong pelancaran pada paparan kedua."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Apl tidak menyokong gelembung"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Pembahagi skrin pisah"</string>
<string name="divider_title" msgid="1963391955593749442">"Pembahagi skrin pisah"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Skrin penuh kiri"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Urus Tetingkap"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tukar nisbah bidang"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimumkan Paparan"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Ikon Ralat"</string>
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Tetingkap desktop)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimumkan Skrin"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ubah saiz"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apl tidak boleh dialihkan ke sini"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Membuka tetingkap apl baharu"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Menutup tetingkap apl"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Meminimumkan tetingkap apl"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Terfokus)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Tidak terfokus)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Mengasyikkan"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Pulihkan"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimumkan"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ဤအက်ပ်ကို ဝင်းဒိုး ၁ ခုတွင်သာ ဖွင့်နိုင်သည်"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ဤအက်ပ်အနေဖြင့် ဒုတိယဖန်သားပြင်ပေါ်တွင် အလုပ်လုပ်မည် မဟုတ်ပါ။"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ဤအက်ပ်အနေဖြင့် ဖွင့်ရန်စနစ်ကို ဒုတိယဖန်သားပြင်မှ အသုံးပြုရန် ပံ့ပိုးမထားပါ။"</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"အက်ပ်သည် ပူဖောင်းကွက်ကို မပံ့ပိုးပါ"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ဝင်းဒိုးများ စီမံရန်"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"အချိုးအစား ပြောင်းရန်"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"မြင်ကွင်းကို ပိုကောင်းအောင်လုပ်ရန်"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"အမှားအယွင်း သင်္ကေတ"</string>
<string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ဒက်စ်တော့ဝင်းဒိုးမုဒ်)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"စခရင်ကို ချဲ့မည်"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"အရွယ်ပြင်ရန်"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"အက်ပ်ကို ဤနေရာသို့ ရွှေ့၍မရပါ"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"အက်ပ်ဝင်းဒိုးအသစ် ဖွင့်နေသည်"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"အက်ပ်ဝင်းဒိုး ပိတ်ခြင်း"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"အက်ပ်ဝင်းဒိုးကို ချုံ့နေသည်"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (အာရုံပြုထားသည်)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (အာရုံပြုမထားပါ)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ဘက်ပေါင်းစုံ"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ပြန်ပြောင်းရန်"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ချဲ့ရန်"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Denne appen kan bare åpnes i ett vindu"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer kanskje ikke på en sekundær skjerm."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke kjøres på sekundære skjermer."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"Appen støtter ikke bobler"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Skilleelement for delt skjerm"</string>
<string name="divider_title" msgid="1963391955593749442">"Skilleelement for delt skjerm"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Utvid den venstre delen av skjermen til hele skjermen"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduene"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Endre høyde/bredde-forholdet"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimaliser visning"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Feilikon"</string>
<string name="close_text" msgid="4986518933445178928">"Lukk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (datamaskin-vindusvisning)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimer skjermen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Endre størrelse"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Appen kan ikke flyttes hit"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Åpner et nytt appvindu"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"Lukker appvinduet"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"Minimerer appvinduet"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fokusert)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ikke fokusert)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Oppslukende"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Gjenopprett"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimer"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"यो एप एउटा विन्डोमा मात्र खोल्न मिल्छ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो एपले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"एपले सहायक प्रदर्शनहरूमा लञ्च सुविधालाई समर्थन गर्दैन।"</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"एपमा बबल प्रयोग गर्न मिल्दैन"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"विन्डोहरू व्यवस्थापन गर्नुहोस्"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"भ्यू अप्टिमाइज गर्नुहोस्"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"त्रुटि जनाउने आइकन"</string>
<string name="close_text" msgid="4986518933445178928">"बन्द गर्नुहोस्"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनु बन्द गर्नुहोस्"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटप विन्डोइङ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रिन ठुलो बनाउनुहोस्"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"आकार बदल्नुहोस्"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"एप सारेर यहाँ ल्याउन सकिएन"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"एपको नयाँ विन्डो खोल्नुहोस्"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"एपको विन्डो बन्द गरिँदै छ"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"एपको विन्डो मिनिमाइज गरिँदै छ"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (फोकस गरिएको)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (फोकस नगरिएको)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"इमर्सिभ"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"रिस्टोर गर्नुहोस्"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ठुलो बनाउनुहोस्"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"Deze app kan maar in 1 venster worden geopend"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App werkt mogelijk niet op een secundair scherm."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App kan niet op secundaire displays worden gestart."</string>
<string name="bubble_not_supported_text" msgid="8843806122229754809">"App ondersteunt geen bubbel"</string>
<string name="accessibility_divider" msgid="6407584574218956849">"Scheiding voor gesplitst scherm"</string>
<string name="divider_title" msgid="1963391955593749442">"Scheiding voor gesplitst scherm"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Linkerscherm op volledig scherm"</string>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"Vensters beheren"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Beeldverhouding wijzigen"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"Weergave optimaliseren"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"Fouticoon"</string>
<string name="close_text" msgid="4986518933445178928">"Sluiten"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menu sluiten"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktopvensterfunctie)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Formaat aanpassen"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Kan de app niet hierheen verplaatsen"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"Nieuw app-venster openen"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"App-venster sluiten"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"App-venster minimaliseren"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (gefocust)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (niet gefocust)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersief"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Herstellen"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximaliseren"</string>
+7
View File
@@ -36,6 +36,7 @@
<string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ଏହି ଆପକୁ କେବଳ 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="bubble_not_supported_text" msgid="8843806122229754809">"ଆପ ବବଲକୁ ସପୋର୍ଟ କରେ ନାହିଁ"</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>
@@ -133,12 +134,18 @@
<string name="manage_windows_text" msgid="5567366688493093920">"ୱିଣ୍ଡୋଗୁଡ଼ିକୁ ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="handle_menu_restart_text" msgid="3907767216238298098">"ଭ୍ୟୁ ଅପ୍ଟିମାଇଜ କରନ୍ତୁ"</string>
<string name="handle_menu_error_icon_text" msgid="7200375184879808684">"ତ୍ରୁଟି ଆଇକନ"</string>
<string name="close_text" msgid="4986518933445178928">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ଡେସ୍କଟପ ୱିଣ୍ଡୋଇଂ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ରିସାଇଜ କରନ୍ତୁ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ଆପକୁ ଏଠାକୁ ମୁଭ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="desktop_mode_talkback_state_opening" msgid="8505494249513712682">"ନୂଆ ଆପ ୱିଣ୍ଡୋ ଖୋଲୁଛି"</string>
<string name="desktop_mode_talkback_state_closing" msgid="6791583518636526904">"ଆପ ୱିଣ୍ଡୋକୁ ବନ୍ଦ କରାଯାଉଛି"</string>
<string name="desktop_mode_talkback_state_minimizing" msgid="7406911785134757827">"ଆପ ୱିଣ୍ଡୋକୁ ଛୋଟ କରନ୍ତୁ"</string>
<string name="desktop_mode_talkback_state_focused" msgid="6287982923335568236">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ଫୋକସ କରାଯାଇଛି)"</string>
<string name="desktop_mode_talkback_state_not_focused" msgid="4640618691603218929">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ଫୋକସ କରାଯାଇନାହିଁ)"</string>
<string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ଇମର୍ସିଭ"</string>
<string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ରିଷ୍ଟୋର କରନ୍ତୁ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ବଡ଼ କରନ୍ତୁ"</string>

Some files were not shown because too many files have changed in this diff Show More